DuoSecurity.psm1
#Region './Private/Argument Completers/DuoAccountCompleter.ps1' 0 $DuoAccountCompleter = { param ( $CommandName, $ParamName, $AccountName, $CommandAst, $fakeBoundParameters ) if (!$script:DuoAccountsList) { Get-DuoAccounts | Out-Null } $AccountName = $AccountName -replace "'",'' ($script:DuoAccountsList).name | Where-Object { $_ -match "$AccountName" } | ForEach-Object { "'$_'" } } $DuoAccountIdCompleter = { param ( $CommandName, $ParamName, $AccountId, $CommandAst, $fakeBoundParameters ) if (!$script:DuoAccountsList) { Get-DuoAccounts | Out-Null } ($script:DuoAccountsList).account_id | Where-Object { $_ -like "$AccountId*" } } Register-ArgumentCompleter -CommandName Select-DuoAccount -ParameterName AccountId -ScriptBlock $DuoAccountIdCompleter Register-ArgumentCompleter -CommandName Select-DuoAccount -ParameterName Name -ScriptBlock $DuoAccountCompleter #EndRegion './Private/Argument Completers/DuoAccountCompleter.ps1' 32 #Region './Private/Argument Completers/DuoAuthProxyLogCompleter.ps1' 0 $DuoAuthProxyLogCompleter = { param ( $CommandName, $ParamName, $LogName, $CommandAst, $fakeBoundParameters ) Get-DuoAuthProxyLogs -ListLogs | Where-Object { $_ -match "$LogName" } } Register-ArgumentCompleter -CommandName Get-DuoAuthProxyLogs -ParameterName LogName -ScriptBlock $DuoAuthProxyLogCompleter #EndRegion './Private/Argument Completers/DuoAuthProxyLogCompleter.ps1' 13 #Region './Private/Argument Completers/DuoIntegrationCompleter.ps1' 0 $DuoIntegrationCompleter = { param ( $CommandName, $ParamName, $Type, $CommandAst, $fakeBoundParameters ) $Types = @' [ { "type": "1password", "Description": "1Password" }, { "type": "accountsapi", "Description": "Accounts API" }, { "type": "adfs", "Description": "Microsoft ADFS" }, { "type": "adminapi", "Description": "Admin API" }, { "type": "aeries", "Description": "Aeries SIS" }, { "type": "agadobe_documentcloud", "Description": "SAML - Adobe Document Cloud" }, { "type": "agaha", "Description": "SAML - Aha!" }, { "type": "agasana", "Description": "SAML - Asana" }, { "type": "agaws", "Description": "SAML - Amazon Web Services" }, { "type": "agatlassian-cloud", "Description": "SAML - Atlassian Cloud" }, { "type": "agbamboohr", "Description": "SAML - BambooHR" }, { "type": "agbarracuda-waf", "Description": "SAML - Barracuda WAF" }, { "type": "agbluejeans", "Description": "SAML - BlueJeans" }, { "type": "agbomgar", "Description": "SAML - Bomgar" }, { "type": "agbonusly", "Description": "SAML - Bonusly" }, { "type": "agbox", "Description": "SAML - Box" }, { "type": "agbugsnag", "Description": "SAML - Bugsnag" }, { "type": "agcanvas", "Description": "SAML - Canvas" }, { "type": "agclarizen", "Description": "SAML - Clarizen" }, { "type": "agcloudlock", "Description": "SAML - CloudLock" }, { "type": "agconfluence", "Description": "SAML - Confluence" }, { "type": "agcrashplan", "Description": "SAML - CrashPlan" }, { "type": "agcyberark", "Description": "SAML - CyberArk Privileged Account Security" }, { "type": "agdatadog", "Description": "SAML - Datadog" }, { "type": "agdesk", "Description": "SAML - Desk" }, { "type": "agdigicert", "Description": "SAML - DigiCert" }, { "type": "agdng", "Description": "SAML - Duo Network Gateway" }, { "type": "agdocusign", "Description": "SAML - DocuSign" }, { "type": "agdropbox", "Description": "SAML - Dropbox" }, { "type": "agduo-adminpanel", "Description": "SAML - Duo Admin Panel" }, { "type": "agegnyte", "Description": "SAML - Egnyte" }, { "type": "agevernote", "Description": "SAML - Evernote" }, { "type": "agexpensify", "Description": "SAML - Expensify" }, { "type": "agfacebook", "Description": "SAML - Workplace by Facebook" }, { "type": "agfreshdesk", "Description": "SAML - Freshdesk" }, { "type": "aggeneric", "Description": "SAML - Service Provider" }, { "type": "aggithub-business", "Description": "SAML - GitHub.com for Business" }, { "type": "aggithub-enterprise", "Description": "SAML - GitHub Enterprise" }, { "type": "aggoogle", "Description": "SAML - Google Workspace" }, { "type": "aggotomeeting", "Description": "SAML - GoToMeeting" }, { "type": "aggreenhouse", "Description": "SAML - Greenhouse" }, { "type": "aghackerone", "Description": "SAML - HackerOne" }, { "type": "aghackerrank", "Description": "SAML - HackerRank for Work" }, { "type": "agheroku", "Description": "SAML - Heroku" }, { "type": "aghipchat", "Description": "SAML - HipChat" }, { "type": "agigloo", "Description": "SAML - Igloo" }, { "type": "agintacct", "Description": "SAML - Intacct" }, { "type": "agjamf-jss", "Description": "SAML - Jamf Pro" }, { "type": "agjira", "Description": "SAML - JIRA" }, { "type": "agjitbit", "Description": "SAML - Jitbit" }, { "type": "aglooker", "Description": "SAML - Looker" }, { "type": "agmarketo", "Description": "SAML - Marketo" }, { "type": "agmeraki", "Description": "SAML - Meraki" }, { "type": "agmonday", "Description": "SAML - Monday" }, { "type": "agnamely", "Description": "SAML - Namely" }, { "type": "agnetdocuments", "Description": "SAML - NetDocuments" }, { "type": "agnewrelic", "Description": "SAML - New Relic" }, { "type": "agoffice365", "Description": "SAML - Office 365" }, { "type": "agopendns", "Description": "SAML - OpenDNS" }, { "type": "agpagerduty", "Description": "SAML - PagerDuty" }, { "type": "agpaloalto-aperture", "Description": "SAML - Palo Alto Networks Aperture" }, { "type": "agpaloalto", "Description": "SAML - Palo Alto Networks" }, { "type": "agremedyforce", "Description": "SAML - Remedyforce" }, { "type": "agringcentral", "Description": "SAML - RingCentral" }, { "type": "agrobin", "Description": "SAML - Robin" }, { "type": "agsalesforce", "Description": "SAML - Salesforce" }, { "type": "agsamanage", "Description": "SAML - Samanage" }, { "type": "agsaucelabs", "Description": "SAML - Saucelabs" }, { "type": "agsharefile", "Description": "SAML - ShareFile" }, { "type": "agsignalsciences", "Description": "SAML - Signal Sciences" }, { "type": "agslack", "Description": "SAML - Slack" }, { "type": "agsmartsheet", "Description": "SAML - Smartsheet" }, { "type": "agstatuspageio", "Description": "SAML - StatusPage.io" }, { "type": "agsugarcrm", "Description": "SAML - SugarCRM" }, { "type": "agsumologic", "Description": "SAML - Sumo Logic" }, { "type": "agsyncplicity", "Description": "SAML - Syncplicity" }, { "type": "agtableau-online", "Description": "SAML - Tableau Online" }, { "type": "agtableau", "Description": "SAML - Tableau" }, { "type": "agudemy", "Description": "SAML - Udemy" }, { "type": "aguservoice", "Description": "SAML - UserVoice" }, { "type": "agwebex", "Description": "SAML - Cisco Webex Meetings (with Site Admin)" }, { "type": "agwebex-controlhub", "Description": "SAML - Cisco Webex (with Control Hub)" }, { "type": "agworkday", "Description": "SAML - Workday" }, { "type": "agzendesk", "Description": "SAML - Zendesk" }, { "type": "agzoom", "Description": "SAML - Zoom" }, { "type": "akamai-eaa", "Description": "Akamai Enterprise Application Access" }, { "type": "array", "Description": "Array SSL VPN" }, { "type": "aws-directory-service", "Description": "AWS Directory Service" }, { "type": "azure-ca", "Description": "Microsoft Azure Active Directory" }, { "type": "authapi", "Description": "Auth API" }, { "type": "barracuda", "Description": "Barracuda SSL VPN" }, { "type": "bitium", "Description": "Bitium" }, { "type": "bitwarden", "Description": "Bitwarden" }, { "type": "bomgar", "Description": "Bomgar" }, { "type": "caradigm", "Description": "Caradigm" }, { "type": "cas", "Description": "CAS (Central Authentication Service)" }, { "type": "checkpoint", "Description": "Check Point VPN" }, { "type": "cisco", "Description": "Cisco ASA SSL VPN" }, { "type": "ciscofirepower", "Description": "Cisco Firepower Threat Defense VPN" }, { "type": "ciscoiseradius", "Description": "Cisco ISE" }, { "type": "ciscoradius", "Description": "Cisco RADIUS VPN" }, { "type": "citrixcag", "Description": "Citrix Access Gateway" }, { "type": "citrixns", "Description": "Citrix Gateway (formerly Citrix NetScaler)" }, { "type": "clearpass", "Description": "Aruba ClearPass" }, { "type": "confluence", "Description": "Confluence" }, { "type": "cyberark", "Description": "CyberArk Privileged Account Security LDAP/RADIUS" }, { "type": "cyberarkweb", "Description": "CyberArk Privileged Account Security" }, { "type": "dag", "Description": "Duo Access Gateway Launcher" }, { "type": "device-management-portal", "Description": "Device Management Portal" }, { "type": "dng-ssh", "Description": "Duo Network Gateway - SSH Servers" }, { "type": "dng", "Description": "Duo Network Gateway - Web Application" }, { "type": "drawbridgenetworks", "Description": "OPAQ 360" }, { "type": "drupal", "Description": "Drupal" }, { "type": "epic", "Description": "Epic Hyperspace" }, { "type": "f5bigip", "Description": "F5 BIG-IP APM" }, { "type": "f5firepass", "Description": "F5 FirePass SSL VPN" }, { "type": "fortinet", "Description": "Fortinet FortiGate SSL VPN" }, { "type": "greyheller", "Description": "Appsian Security Platform" }, { "type": "jira", "Description": "JIRA" }, { "type": "juniper", "Description": "Juniper SSL VPN" }, { "type": "juniperuac", "Description": "Juniper UAC" }, { "type": "keeper", "Description": "Keeper Security" }, { "type": "labtech", "Description": "LabTech Software" }, { "type": "lastpass", "Description": "LastPass" }, { "type": "ldapproxy", "Description": "LDAP Proxy" }, { "type": "macos", "Description": "macOS" }, { "type": "merakiradius", "Description": "Meraki RADIUS VPN" }, { "type": "myworkdrive", "Description": "MyWorkDrive" }, { "type": "netmotion", "Description": "NetMotion Mobility" }, { "type": "oam", "Description": "Oracle Access Manager" }, { "type": "okta", "Description": "Okta" }, { "type": "onelogin", "Description": "OneLogin" }, { "type": "openvpn", "Description": "OpenVPN" }, { "type": "openvpnas", "Description": "OpenVPN Access Server" }, { "type": "owa", "Description": "Microsoft OWA" }, { "type": "paloalto", "Description": "Palo Alto SSL VPN" }, { "type": "partner_authapi", "Description": "Partner Auth API" }, { "type": "partner_websdk", "Description": "Partner WebSDK" }, { "type": "pingfederate", "Description": "PingFederate" }, { "type": "portal", "Description": "User Self-enrollment Portal (Bulk and Email enrollment)" }, { "type": "radius", "Description": "RADIUS" }, { "type": "rdgateway", "Description": "Microsoft RD Gateway" }, { "type": "rdp", "Description": "Microsoft RDP" }, { "type": "rdweb", "Description": "Microsoft RD Web" }, { "type": "resilient", "Description": "Resilient Systems" }, { "type": "rest", "Description": "Auth API" }, { "type": "rras", "Description": "Microsoft RRAS" }, { "type": "sailpoint", "Description": "SailPoint API" }, { "type": "sailpointweb", "Description": "SailPoint Web" }, { "type": "samlidp", "Description": "SAML IdP" }, { "type": "shibboleth", "Description": "Shibboleth" }, { "type": "sonicwallsra", "Description": "SonicWALL SRA SSL VPN" }, { "type": "sophosutm", "Description": "Sophos UTM" }, { "type": "splunk", "Description": "Splunk" }, { "type": "thycotic", "Description": "Thycotic Secret Server" }, { "type": "tmg", "Description": "Microsoft TMG" }, { "type": "uag", "Description": "Microsoft UAG" }, { "type": "unix", "Description": "Unix Application" }, { "type": "verify", "Description": "Verify API" }, { "type": "vmwareview", "Description": "VMWare View" }, { "type": "websdk", "Description": "Web SDK" }, { "type": "workday", "Description": "Workday" }, { "type": "wordpress", "Description": "WordPress" } ] '@ | ConvertFrom-Json $Types.type | Where-Object { $_ -like "$Type*" } } Register-ArgumentCompleter -CommandName New-DuoIntegration -ParameterName Type -ScriptBlock $DuoIntegrationCompleter #EndRegion './Private/Argument Completers/DuoIntegrationCompleter.ps1' 220 #Region './Private/Base64/ConvertFrom-Base64.ps1' 0 function ConvertFrom-Base64 { <# .SYNOPSIS Convert Base64 string to file .DESCRIPTION Converts file to a byte stream and writes the contents as base64 .PARAMETER Base64 File contents in Base64 string .PARAMETER Path Path to the file .EXAMPLE ConvertTo-Base64 -Path path/to/file.png #> Param( [Parameter(Mandatory = $true)] [String]$Base64, [Parameter(Mandatory = $true)] [String]$Path ) $Bytes = [Convert]::FromBase64String($Base64) [IO.File]::WriteAllBytes($Path, $Bytes) } #EndRegion './Private/Base64/ConvertFrom-Base64.ps1' 28 #Region './Private/Base64/ConvertTo-Base64.ps1' 0 function ConvertTo-Base64 { <# .SYNOPSIS Convert file to Base64 .DESCRIPTION Converts file to a byte stream and writes the contents as base64 .PARAMETER Path Path to the file .EXAMPLE ConvertTo-Base64 -Path path/to/file.png #> Param( [Parameter(Mandatory = $true)] [String]$Path ) [convert]::ToBase64String((Get-Content -LiteralPath $Path -AsByteStream -Raw)) } #EndRegion './Private/Base64/ConvertTo-Base64.ps1' 21 #Region './Private/Images/Test-PngFile.ps1' 0 function Test-PngFile { [CmdletBinding()] Param( [Parameter()] [string]$Path ) $PngHeaders = @( '89', '50', '4E', '47', '0D', '0A', '1A', '0A' ); $Bytes = Get-Content -LiteralPath $Path -AsByteStream -ReadCount 1 -TotalCount 8 -ErrorAction Ignore $FileHeader = $Bytes | Select-Object -First $PngHeaders.Length | ForEach-Object { $_.ToString('X2') } $Diff = Compare-Object -ReferenceObject $PngHeaders -DifferenceObject $FileHeader ($Diff | Measure-Object).Count -eq 0 } #EndRegion './Private/Images/Test-PngFile.ps1' 13 #Region './Private/REST Handler/Invoke-DuoPaginatedRequest.ps1' 0 function Invoke-DuoPaginatedRequest { <# .SYNOPSIS Paginated requests to Duo API .DESCRIPTION Wraps Invoke-DuoRequest setting offset to next_offset .PARAMETER DuoRequest Request to paginate #> [CmdletBinding()] Param( $DuoRequest ) do { $Request = Invoke-DuoRequest @DuoRequest $Request.response if ($Request.metadata.next_offset) { $DuoRequest.Params.offset = $Request.metadata.next_offset } } while ($Request.metadata.next_offset -and $Request.stat -eq 'OK') if ($Request.stat -ne 'OK') { $Request } } #EndRegion './Private/REST Handler/Invoke-DuoPaginatedRequest.ps1' 30 #Region './Private/REST Handler/Invoke-DuoRequest.ps1' 0 function Invoke-DuoRequest { <# .SYNOPSIS Main Duo API function .DESCRIPTION Calls Duo API with signed token for request .PARAMETER Method GET,POST,DELETE .PARAMETER Path Path to API endpoint .PARAMETER Params Hashtable of parameters .PARAMETER NoAuth Do not send authorization header .PARAMETER FilePath Path to save output file to .EXAMPLE Invoke-DuoRequest -Path '/admin/v1/users' -Method GET #> [CmdletBinding()] Param( [Parameter()] [string]$Method = 'GET', [Parameter()] [ValidateNotNullOrEmpty()] [string]$Path, [Parameter()] [hashtable]$Params = @{}, [Parameter()] [switch]$NoAuth, [Parameter()] [string]$FilePath ) # Get API credentials if ($Path -match '/admin' -and $Script:DuoApiHost) { $ApiHost = $script:DuoApiHost $IntegrationKey = $script:DuoIntegrationKey $SecretKey = $script:DuoSecretKey if ($script:DuoAccountId) { $AccountId = $script:DuoAccountId } } # Replace api host with parent account api host when switching between accounts elseif ($Path -match '/accounts' -and $script:DuoAccountsApiHost) { $AccountId = '' $ApiHost = $script:DuoAccountsApiHost $IntegrationKey = $script:DuoAccountsIntegrationKey $SecretKey = $script:DuoAccountsSecretKey } # Replace api host with parent account api host when switching between accounts elseif ($Path -match '/auth' -and $script:DuoAuthIntegrationKey) { $AccountId = '' $ApiHost = $script:DuoAuthApiHost $IntegrationKey = $script:DuoAuthIntegrationKey $SecretKey = $script:DuoAuthSecretKey } # Check credentials exists if (!$ApiHost -or !$IntegrationKey -or !$SecretKey) { Write-Error 'API Credentials not set, run Set-DuoApiAuth' return $false } # RFC 2822 date format in UTC $XDuoDate = (Get-Date).ToUniversalTime().ToString('ddd, dd MMM yyyy HH:mm:ss -0000') # Assemble parameters $ParamCollection = [System.Web.HttpUtility]::ParseQueryString([String]::Empty) if ($AccountId) { Write-Verbose "account_id = $AccountId" $ParamCollection.Add('account_id', $AccountId) } # Sort parameters foreach ($Item in ($Params.GetEnumerator() | Sort-Object -CaseSensitive -Property Key)) { $ParamCollection.Add($Item.Key, $Item.Value) } # Query string $Request = $ParamCollection.ToString() -replace '%7E', '~' -replace '\+', '%20' $Request = [regex]::Replace($Request, '(%[0-9A-Fa-f][0-9A-Fa-f])', { $args[0].Value.ToUpperInvariant() }) $Request = [regex]::Replace($Request, "([!'()*])", { '%' + [System.Convert]::ToByte($args[0].Value[0]).ToString('X') }) # Build Duo signature body linefeed separated $SignatureParts = @( $XDuoDate $Method.ToUpper() $ApiHost.ToLower() $Path $Request ) $SignatureBody = $SignatureParts -join "`n" # Encode signature with secretbytes [byte[]]$KeyBytes = [System.Text.Encoding]::UTF8.GetBytes($SecretKey.ToCharArray()) [byte[]]$DataBytes = [System.Text.Encoding]::UTF8.GetBytes($SignatureBody.ToCharArray()) # Generate an HMAC SHA1 hash $HmacSha1 = New-Object System.Security.Cryptography.HMACSHA1 $HmacSha1.Key = $KeyBytes $null = $HmacSha1.ComputeHash($DataBytes) $HashHex = [System.BitConverter]::ToString($HmacSha1.Hash) $Signature = $HashHex.Replace('-', '').ToLower() # Build base64 encoded auth string with IntegrationKey and Signature $AuthString = 'Basic ' + [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes(('{0}:{1}' -f $IntegrationKey, $Signature))) # Assembled auth headers $Headers = @{ 'X-Duo-Date' = $XDuoDate 'Authorization' = $AuthString } if ($Method -eq 'POST') { $Headers.'Content-Type' = 'application/x-www-form-urlencoded' $Body = $Request Write-Verbose $Request } if ($NoAuth) { $Headers = @{} } # Make API call URI $UriBuilder = [System.UriBuilder]('https://{0}{1}' -f $ApiHost, $Path) if ($Method -ne 'POST') { $UriBuilder.Query = $Request } Write-Verbose ( '{0} [{1}]' -f $Method, $UriBuilder.Uri ) $RestMethod = @{ Method = $Method Uri = $UriBuilder.Uri Headers = $Headers SkipHttpErrorCheck = $true } if ($Body) { $RestMethod.Body = $Body } if ($FilePath) { $RestMethod.OutFile = $FilePath } $Results = Invoke-RestMethod @RestMethod $Results } #EndRegion './Private/REST Handler/Invoke-DuoRequest.ps1' 167 #Region './Public/Admin API/Users/Add-DuoUserPhone.ps1' 0 function Add-DuoUserPhone { <# .SYNOPSIS Associate Phone with User .DESCRIPTION Associate a phone with the user with ID user_id. Requires "Grant write resource" API permission. Object limits: 100 phones per user; 100 users per phone. .PARAMETER UserId The ID of the user .PARAMETER PhoneId The ID of the phone to associate with the user. .EXAMPLE Add-DuoUserPhone -UserId SOMEUSERID -PhoneId SOMEPHONEID .LINK https://duo.com/docs/adminapi#associate-phone-with-user #> [CmdletBinding()] Param( [Parameter(ValueFromPipelineByPropertyName = $true, Mandatory = $true)] [Alias('user_id')] [string]$UserId, [Parameter(ValueFromPipelineByPropertyName = $true, Mandatory = $true)] [Alias('phone_id')] [string]$PhoneId ) process { $DuoRequest = @{ Method = 'POST' Path = '/admin/v1/users/{0}/phones' -f $UserId Params = @{ phone_id = $PhoneId } } Invoke-DuoRequest @DuoRequest } } #EndRegion './Public/Admin API/Users/Add-DuoUserPhone.ps1' 46 #Region './Public/Admin API/Users/Add-DuoUserToGroup.ps1' 0 function Add-DuoUserToGroup { <# .SYNOPSIS Associate Group with User .DESCRIPTION Associate a group with ID group_id with the user with ID user_id. Requires "Grant write resource" API permission. Object limits: 100 groups per user. .PARAMETER UserId The ID of the user .PARAMETER GroupId The ID of the group to associate with the user. .EXAMPLE Add-DuoUserToGroup -UserId SOMEUSERID -GroupId SOMEGROUPID .LINK https://duo.com/docs/adminapi#associate-group-with-user #> [CmdletBinding()] Param( [Parameter(ValueFromPipelineByPropertyName = $true, Mandatory = $true)] [Alias('user_id')] [string]$UserId, [Parameter(ValueFromPipelineByPropertyName = $true, Mandatory = $true)] [Alias('group_id')] [string]$GroupId ) process { $DuoRequest = @{ Method = 'POST' Path = '/admin/v1/users/{0}/groups' -f $UserId Params = @{ group_id = $GroupId } } Invoke-DuoRequest @DuoRequest } } #EndRegion './Public/Admin API/Users/Add-DuoUserToGroup.ps1' 46 #Region './Public/Admin API/Users/Add-DuoUserToken.ps1' 0 function Add-DuoUserToken { <# .SYNOPSIS Associate Hardware Token with User .DESCRIPTION Associate a hardware token with the user with ID user_id. Requires "Grant write resource" API permission. Object limits: 100 tokens per user. .PARAMETER UserId The ID of the user .PARAMETER TokenId The ID of the hardware token to associate with the user. .EXAMPLE Add-DuoUserToken -UserId SOMEUSERID -TokenId SOMETOKENID .LINK https://duo.com/docs/adminapi#associate-hardware-token-with-user .NOTES #> [CmdletBinding()] Param( [Parameter(ValueFromPipelineByPropertyName = $true, Mandatory = $true)] [Alias('user_id')] [string]$UserId, [Parameter(ValueFromPipelineByPropertyName = $true, Mandatory = $true)] [Alias('token_id')] [string]$TokenId ) process { $DuoRequest = @{ Method = 'POST' Path = '/admin/v1/users/{0}/tokens' -f $UserId Params = @{ token_id = $TokenId } } Invoke-DuoRequest @DuoRequest } } #EndRegion './Public/Admin API/Users/Add-DuoUserToken.ps1' 48 #Region './Public/Admin API/Users/Get-DuoUserBypassCodes.ps1' 0 function Get-DuoUserBypassCodes { <# .SYNOPSIS Retrieve Bypass Codes by User ID .DESCRIPTION Returns a paged list of bypass code metadata associated with the user with ID user_id. Does not return the actual bypass codes. Requires "Grant read resource" API permission. .PARAMETER UserId The ID of the user .EXAMPLE Get-DuoUserBypassCodes -UserId SOMEUSERID .LINK https://duo.com/docs/adminapi#retrieve-bypass-codes-by-user-id .NOTES #> [CmdletBinding()] Param( [Parameter(ValueFromPipelineByPropertyName = $true, Mandatory = $true)] [Alias('user_id')] [string]$UserId ) process { $DuoRequest = @{ Method = 'GET' Path = '/admin/v1/users/{0}/bypass_codes' -f $UserId } Invoke-DuoPaginatedRequest -DuoRequest $DuoRequest } } #EndRegion './Public/Admin API/Users/Get-DuoUserBypassCodes.ps1' 36 #Region './Public/Admin API/Users/Get-DuoUserGroups.ps1' 0 function Get-DuoUserGroups { <# .SYNOPSIS Retrieve Groups by User ID .DESCRIPTION Returns a paged list of groups associated with the user with ID user_id. Requires "Grant read resource" API permission. .PARAMETER UserId The ID of the User .EXAMPLE Get-DuoUserGroups -UserId SOME USERID .LINK https://duo.com/docs/adminapi#retrieve-groups-by-user-id #> [CmdletBinding()] Param( [Parameter(ValueFromPipelineByPropertyName = $true, Mandatory = $true)] [Alias('user_id')] [string]$UserId ) process { $DuoRequest = @{ Method = 'GET' Path = '/admin/v1/users/{0}/groups' -f $UserId } Invoke-DuoPaginatedRequest -DuoRequest $DuoRequest } } #EndRegion './Public/Admin API/Users/Get-DuoUserGroups.ps1' 34 #Region './Public/Admin API/Users/Get-DuoUserPhones.ps1' 0 function Get-DuoUserPhones { <# .SYNOPSIS Retrieve Phones by User ID .DESCRIPTION Returns a paged list of phones associated with the user with ID user_id. Requires "Grant read resource" API permission. .PARAMETER UserId THe ID of the user .EXAMPLE Get-DuoUserPhones -UserId SOMEUSERID .LINK https://duo.com/docs/adminapi#retrieve-phones-by-user-id #> [CmdletBinding()] Param( [Parameter(ValueFromPipelineByPropertyName = $true, Mandatory = $true)] [Alias('user_id')] [string]$UserId ) process { $DuoRequest = @{ Method = 'GET' Path = '/admin/v1/users/{0}/phones' -f $UserId } Invoke-DuoPaginatedRequest -DuoRequest $DuoRequest } } #EndRegion './Public/Admin API/Users/Get-DuoUserPhones.ps1' 34 #Region './Public/Admin API/Users/Get-DuoUsers.ps1' 0 function Get-DuoUsers { <# .SYNOPSIS Retrieve Users .DESCRIPTION Returns a single user or a paged list of users. If username is not provided, the list will contain all users. If username is provided, the list will either contain a single user (if a match was found) or no users. Requires "Grant read resource" API permission. .PARAMETER UserId Specify a user id .PARAMETER Username Specify a user name (or username alias) to look up a single user. .EXAMPLE Get-DuoUser -Username bob .LINK https://duo.com/docs/adminapi#retrieve-users .LINK https://duo.com/docs/adminapi#retrieve-user-by-id #> [CmdletBinding(DefaultParameterSetName = 'List')] Param( [Parameter(ValueFromPipelineByPropertyName = $true, Mandatory = $true, ParameterSetName = 'Single')] [Alias('user_id')] [string]$UserId, [Parameter(ParameterSetName = 'List')] [string]$Username ) process { switch ($PSCmdlet.ParameterSetName) { 'List' { $Path = '/admin/v1/users' } 'Single' { $Path = '/admin/v1/users/{0}' -f $UserId } } $Params = @{} if ($Username) { $Params.username = $Username } $DuoRequest = @{ Method = 'GET' Path = $Path Params = $Params } switch ($PSCmdlet.ParameterSetName) { 'List' { Invoke-DuoPaginatedRequest -DuoRequest $DuoRequest } 'Single' { $Request = Invoke-DuoRequest @DuoRequest if ($Request.stat -ne 'OK') { $Request } else { $Request.response } } } } } Set-Alias -Name Get-DuoUser -Value Get-DuoUsers #EndRegion './Public/Admin API/Users/Get-DuoUsers.ps1' 73 #Region './Public/Admin API/Users/Get-DuoUserTokens.ps1' 0 function Get-DuoUserTokens { <# .SYNOPSIS Retrieve Hardware Tokens by User ID .DESCRIPTION Returns a paged list of OTP hardware tokens associated with the user with ID user_id. To fetch all results, call repeatedly with the offset parameter as long as the result metadata has a next_offset value. Requires "Grant read resource" API permission. .PARAMETER UserId The ID of the user .EXAMPLE Get-DuoUserTokens -UserId SOMEUSERID .LINK https://duo.com/docs/adminapi#retrieve-hardware-tokens-by-user-id #> [CmdletBinding()] Param( [Parameter(ValueFromPipelineByPropertyName = $true, Mandatory = $true)] [Alias('user_id')] [string]$UserId ) process { $DuoRequest = @{ Method = 'GET' Path = '/admin/v1/users/{0}/tokens' -f $UserId } Invoke-DuoPaginatedRequest -DuoRequest $DuoRequest } } #EndRegion './Public/Admin API/Users/Get-DuoUserTokens.ps1' 34 #Region './Public/Admin API/Users/Get-DuoUserWebAuthnCredentials.ps1' 0 function Get-DuoUserWebAuthnCredentials { <# .SYNOPSIS Retrieve WebAuthn Credentials by User ID .DESCRIPTION Returns a list of WebAuthn credentials associated with the user with ID user_id. Requires "Grant read resource" API permission. .PARAMETER UserId The User ID to use .EXAMPLE Get-DuoUserWebAuthnCredentials -UserId SOMEUSERID .LINK https://duo.com/docs/adminapi#retrieve-webauthn-credentials-by-user-id #> [CmdletBinding()] Param( [Parameter(ValueFromPipelineByPropertyName = $true, Mandatory = $true)] [Alias('user_id')] [string]$UserId ) process { $DuoRequest = @{ Method = 'GET' Path = '/admin/v1/users/{0}/webauthncredentials' -f $UserId } Invoke-DuoPaginatedRequest -DuoRequest $DuoRequest } } #EndRegion './Public/Admin API/Users/Get-DuoUserWebAuthnCredentials.ps1' 34 #Region './Public/Admin API/Users/New-DuoUser.ps1' 0 function New-DuoUser { <# .SYNOPSIS Create User .DESCRIPTION Create a new user with the specified username. Requires "Grant write resource" API permission. .PARAMETER Username The name of the user to create. .PARAMETER Aliases Username aliases for the user. Up to eight aliases may be specified with this parameter as a set of URL-encoded key-value pairs e.g. alias1=joe.smith&alias2=jsmith@example.com. Ignores alias position values not specified. Aliases must be unique amongst users. .PARAMETER FullName The real name (or full name) of this user. .PARAMETER Email The email address of this user. .PARAMETER Status The user's status. One of: Status Description ------ ----------- "active" The user must complete secondary authentication. This is the default value if no status is specified. "bypass" The user will bypass secondary authentication after completing primary authentication. "disabled" The user will not be able to complete secondary authentication. .PARAMETER Notes An optional description or notes field. Can be viewed in the Duo Admin Panel. .PARAMETER FirstName The user's given name. .PARAMETER LastName The user's surname. .EXAMPLE New-DuoUser -Username bob -Aliases @{alias1='bobby'; alias2='robert'} -Status Active .LINK https://duo.com/docs/adminapi#create-user #> [CmdletBinding(SupportsShouldProcess)] Param( [Parameter(Mandatory = $true)] [string]$Username, [Parameter()] [hashtable]$Aliases = @{}, [Parameter()] [string]$FullName = '', [Parameter()] [string]$Email = '', [Parameter()] [ValidateSet('Active', 'Bypass', 'Disabled')] [string]$Status = 'Active', [Parameter()] [string]$Notes = '', [Parameter()] [string]$FirstName = '', [Parameter()] [string]$LastName = '' ) if ($Aliases) { $AliasCollection = [System.Web.HttpUtility]::ParseQueryString([String]::Empty) foreach ($Item in ($Aliases.GetEnumerator() | Sort-Object -CaseSensitive -Property Key)) { $AliasCollection.Add($Item.Key, $Item.Value) } $AliasString = [System.Web.HttpUtility]::UrlDecode($AliasCollection.ToString()) } $Params = @{ username = $Username } if ($Aliases) { $Params.aliases = $AliasString } if ($FullName) { $Params.realname = $FullName } if ($Email) { $Params.email = $Email } if ($Status) { $Params.status = $Status.ToLower() } if ($Notes) { $Params.notes = $Notes } if ($FirstName) { $Params.firstname = $FirstName } if ($LastName) { $Params.lastname = $LastName } $DuoRequest = @{ Method = 'POST' Path = '/admin/v1/users' Params = $Params } if ($PSCmdlet.ShouldProcess($Username)) { $Request = Invoke-DuoRequest @DuoRequest if ($Request.stat -ne 'OK') { $Request } else { $Request.response } } } #EndRegion './Public/Admin API/Users/New-DuoUser.ps1' 109 #Region './Public/Admin API/Users/New-DuoUserBypassCodes.ps1' 0 function New-DuoUserBypassCodes { <# .SYNOPSIS Create Bypass Codes for User .DESCRIPTION Clear all existing bypass codes for the user with ID user_id and return a list of count newly generated bypass codes, or specify codes that expire after valid_secs seconds, or reuse_count uses. Requires "Grant write resource" API permission. Object limits: 100 bypass codes per user. .PARAMETER UserId The ID of the User .PARAMETER Count Number of new bypass codes to create. At most 10 codes (the default) can be created at a time. Codes will be generated randomly. .PARAMETER Codes CSV string of codes to use. Mutually exclusive with count. .PARAMETER ReuseCount The number of times generated bypass codes can be used. If 0, the codes will have an infinite reuse_count. Default: 1 .PARAMETER ValidSecs The number of seconds for which generated bypass codes remain valid. If 0 (the default) the codes will never expire. .EXAMPLE New-DuoUserBypassCodes -UserId SOMEUSERID -Count 1 -ValidSecs 30 .LINK https://duo.com/docs/adminapi#create-bypass-codes-for-user #> [CmdletBinding(SupportsShouldProcess, DefaultParameterSetName = 'Count')] Param( [Parameter(ValueFromPipelineByPropertyName = $true, Mandatory = $true)] [Alias('user_id')] [string]$UserId, [Parameter(ParameterSetName = 'Count')] [ValidateRange(1, 10)] [int]$Count = 10, [Parameter(ParameterSetName = 'Codes')] [string[]]$Codes = @(), [Parameter()] [int]$ReuseCount = 1, [Parameter()] [int]$ValidSecs = 0 ) process { $Params = @{ reuse_count = $ReuseCount valid_secs = $ValidSecs } if ($Codes) { $Params.codes = $Codes -join ',' } else { $Params.count = $Count } $DuoRequest = @{ Method = 'POST' Path = '/admin/v1/users/{0}/bypass_codes' -f $UserId Params = $Params } if ($PSCmdlet.ShouldProcess($UserId)) { $Request = Invoke-DuoRequest @DuoRequest if ($Request.stat -ne 'OK') { $Request } else { $Request.response } } } } #EndRegion './Public/Admin API/Users/New-DuoUserBypassCodes.ps1' 81 #Region './Public/Admin API/Users/Register-DuoUser.ps1' 0 function Register-DuoUser { <# .SYNOPSIS Enroll User .DESCRIPTION Enroll a user with user name username and email address email and send them an enrollment email that expires after valid_secs seconds. Requires "Grant write resource" API permission. .PARAMETER Username The user name (or username alias) of the user to enroll. .PARAMETER Email The email address of this user. .PARAMETER ValidSecs The number of seconds the enrollment code should remain valid. Default: 2592000 (30 days). .EXAMPLE Register-DuoUser -Username 'bill' -Email 'bill.lumbergh@initech.com' .LINK https://duo.com/docs/adminapi#enroll-user .NOTES #> [CmdletBinding(SupportsShouldProcess)] Param( [Parameter(Mandatory = $true)] [string]$Username, [Parameter(Mandatory = $true)] [string]$Email, [Parameter()] [int]$ValidSecs = 2592000 ) $DuoRequest = @{ Method = 'POST' Path = '/admin/v1/users/enroll' Params = @{ username = $Username email = $Email valid_secs = $ValidSecs } } if ($PSCmdlet.ShouldProcess($Email)) { $Request = Invoke-DuoRequest @DuoRequest if ($Request.stat -ne 'OK') { $Request } else { $Request.response } } } #EndRegion './Public/Admin API/Users/Register-DuoUser.ps1' 58 #Region './Public/Admin API/Users/Remove-DuoUser.ps1' 0 function Remove-DuoUser { <# .SYNOPSIS Delete User .DESCRIPTION Delete the user with ID user_id from the system. The API will not automatically delete phones associated with the user; remove them permanently with Delete Phone. This method returns 200 if the phone was found or if no such phone exists. Requires "Grant write resource" API permission. .PARAMETER UserId The ID of the User .EXAMPLE Remove-DuoUser -UserId SOMEUSERID .LINK https://duo.com/docs/adminapi#delete-user .NOTES Users deleted by the API do not get moved into the Trash view as "Pending Deletion" as they would if removed by directory sync, inactive user expiration, or interactively from the Duo Admin Panel, and therefore are not available for restoration. Users deleted via the API are immediately and permanently removed from Duo. #> [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')] Param( [Parameter(ValueFromPipelineByPropertyName = $true, Mandatory = $true)] [Alias('user_id')] [string]$UserId ) process { $DuoRequest = @{ method = 'DELETE' path = '/admin/v1/users/{0}' -f $UserId } if ($PSCmdlet.ShouldProcess($UserId)) { Invoke-DuoRequest @DuoRequest } } } #EndRegion './Public/Admin API/Users/Remove-DuoUser.ps1' 39 #Region './Public/Admin API/Users/Remove-DuoUserFromGroup.ps1' 0 function Remove-DuoUserFromGroup { <# .SYNOPSIS Disassociate Group from User .DESCRIPTION Disassociate a group from the user with ID user_id. This method will return 200 if the group was found or if no such group exists. Requires "Grant write resource" API permission. .PARAMETER UserId The ID of the User .PARAMETER GroupId The ID of the Group .EXAMPLE Remove-DuoUserFromGroup -UserId SOMEUSERID -GroupId SOMEGROUPID .LINK https://duo.com/docs/adminapi#disassociate-group-from-user #> [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')] Param( [Parameter(ValueFromPipelineByPropertyName = $true, Mandatory = $true)] [Alias('user_id')] [string]$UserId, [Parameter(Mandatory = $true)] [string]$GroupId ) process { $DuoRequest = @{ method = 'DELETE' path = '/admin/v1/users/{0}/groups/{1}' -f $UserId, $GroupId } if ($PSCmdlet.ShouldProcess($GroupId)) { Invoke-DuoRequest @DuoRequest } } } #EndRegion './Public/Admin API/Users/Remove-DuoUserFromGroup.ps1' 42 #Region './Public/Admin API/Users/Remove-DuoUserPhone.ps1' 0 function Remove-DuoUserPhone { <# .SYNOPSIS Disassociate Phone from User .DESCRIPTION Disassociate a phone from the user with ID user_id. The API will not automatically delete the phone after removing the last user association; remove it permanently with Delete Phone. This method returns 200 if the phone was found or if no such phone exists. Requires "Grant write resource" API permission. .PARAMETER UserId The ID of the User .PARAMETER PhoneId The ID of the Phone .EXAMPLE Remove-DuoUserPhone -UserId SOMEUSERID -PhoneId SOMEPHONEID .LINK https://duo.com/docs/adminapi#disassociate-phone-from-user #> [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')] Param( [Parameter(ValueFromPipelineByPropertyName = $true, Mandatory = $true)] [Alias('user_id')] [string]$UserId, [Parameter(ValueFromPipelineByPropertyName = $true, Mandatory = $true)] [Alias('phone_id')] [string]$PhoneId ) process { $DuoRequest = @{ method = 'DELETE' path = '/admin/v1/users/{0}/phones/{1}' -f $UserId, $PhoneId } if ($PSCmdlet.ShouldProcess($PhoneIds)) { Invoke-DuoRequest @DuoRequest } } } #EndRegion './Public/Admin API/Users/Remove-DuoUserPhone.ps1' 43 #Region './Public/Admin API/Users/Remove-DuoUserToken.ps1' 0 function Remove-DuoUserToken { <# .SYNOPSIS Disassociate Hardware Token from User .DESCRIPTION Disassociate a hardware token from the user with ID user_id. This method will return 200 if the hardware token was found or if no such hardware token exists. Requires "Grant write resource" API permission. .PARAMETER UserId The ID of the User .PARAMETER TokenId The ID of the Token .EXAMPLE Remove-DuoUserToken -UserId SOMEUSERID -TokenId SOMETOKENID .LINK https://duo.com/docs/adminapi#disassociate-hardware-token-from-user .NOTES #> [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')] Param( [Parameter(ValueFromPipelineByPropertyName = $true, Mandatory = $true)] [Alias('user_id')] [string]$UserId, [Parameter(ValueFromPipelineByPropertyName = $true, Mandatory = $true)] [Alias('token_id')] [string]$TokenId ) process { $DuoRequest = @{ method = 'DELETE' path = '/admin/v1/users/{0}/tokens/{1}' -f $UserId, $TokenId } if ($PSCmdlet.ShouldProcess($TokenId)) { Invoke-DuoRequest @DuoRequest } } } #EndRegion './Public/Admin API/Users/Remove-DuoUserToken.ps1' 45 #Region './Public/Admin API/Users/Sync-DuoUserFromDirectory.ps1' 0 function Sync-DuoUserFromDirectory { <# .SYNOPSIS Synchronize User from Directory .DESCRIPTION Initiate a sync to create, update, or mark for deletion the user specified by username against the directory specified by the directory_key. The directory_key for a directory can be found by navigating to Users -> Directory Sync in the Duo Admin Panel, and then clicking on the configured directory. Learn more about syncing individual users from Active Directory, OpenLDAP, or Azure Active Directory. Requires "Grant write resource" API permission. .PARAMETER DirectoryKey The directory key to sync from .PARAMETER Username The user to update or create via directory sync. This should be the same as the value for the user's username attribute in the source directory as configured in the sync. .EXAMPLE Sync-DuoUserFromDirectory -DirectoryKey 123456 -Username mbolton .LINK https://duo.com/docs/adminapi#synchronize-admin-from-directory #> [CmdletBinding(SupportsShouldProcess)] Param( [Parameter(Mandatory = $true)] [Alias('directory_key')] [string]$DirectoryKey, [Parameter(Mandatory = $true)] [string]$Username ) Process { $Params = @{ username = $Username } $DuoRequest = @{ Method = 'POST' Path = '/admin/v1/users/directorysync/{0}/syncuser' -f $DirectoryKey Params = $Params } if ($PSCmdlet.ShouldProcess($Email)) { $Request = Invoke-DuoRequest @DuoRequest if ($Request.stat -ne 'OK') { $Request } else { $Request.response } } } } #EndRegion './Public/Admin API/Users/Sync-DuoUserFromDirectory.ps1' 53 #Region './Public/Admin API/Users/Update-DuoUser.ps1' 0 function Update-DuoUser { <# .SYNOPSIS Modify User .DESCRIPTION Change the username, username aliases, full name, status, and/or notes section of the user with ID user_id. Requires "Grant write resource" API permission. .PARAMETER UserId The ID of the User .PARAMETER Username The new username. .PARAMETER Aliases Username aliases for the user. Up to eight aliases may be specified with this parameter as a set of URL-encoded key-value pairs e.g. alias1=joe.smith&alias2=jsmith@example.com. Ignores alias position values not specified. Remove the value for an existing alias by specifying a blank value e.g. alias1=. Aliases must be unique amongst users. .PARAMETER FullName The new real name (or full name). .PARAMETER Email The new email address. .PARAMETER Status The new status. Must be one of "active", "disabled", or "bypass". See Retrieve Users for an explanation of these fields. .PARAMETER Notes The new notes field. .PARAMETER FirstName The user's new given name. .PARAMETER LastName The user's new surname. .EXAMPLE Update-DuoUser -UserId SOMEUSERID -Status Disabled .LINK https://duo.com/docs/adminapi#modify-user #> [CmdletBinding(SupportsShouldProcess)] Param( [Parameter(ValueFromPipelineByPropertyName = $true, Mandatory = $true)] [Alias('user_id')] [string]$UserId, [Parameter()] [string]$Username, [Parameter()] [string[]]$Aliases, [Parameter()] [string]$FullName, [Parameter()] [string]$Email, [Parameter()] [ValidateSet('Active', 'Bypass', 'Disabled')] [string]$Status, [Parameter()] [string]$Notes, [Parameter()] [string]$FirstName, [Parameter()] [string]$LastName ) process { $Params = @{} if ($Aliases) { $x = 1 $AliasList = foreach ($Alias in $Aliases) { if ($x -gt 8) { break } @{ "alias$x" = $Alias } $x++ } $Params.aliases = $AliasList } if ($Username) { $Params.username = $Username } if ($FullName) { $Params.realname = $FullName } if ($Email) { $Params.email = $Email } if ($Status) { $Params.status = $Status.ToLower() } if ($Notes) { $Params.notes = $Notes } if ($FirstName) { $Params.firstname = $FirstName } if ($LastName) { $Params.lastname = $LastName } $DuoRequest = @{ Method = 'POST' Path = '/admin/v1/users/{0}' -f $UserId Params = $Params } if ($PSCmdlet.ShouldProcess($UserId)) { $Request = Invoke-DuoRequest @DuoRequest if ($Request.stat -ne 'OK') { $Request } else { $Request.response } } } } #EndRegion './Public/Admin API/Users/Update-DuoUser.ps1' 111 #Region './Public/Admin API/Integrations/Get-DuoIntegrations.ps1' 0 function Get-DuoIntegrations { <# .SYNOPSIS Retrieve Integrations .DESCRIPTION Returns a single integration or a paged list of integrations. Requires "Grant read resource" API permission. .PARAMETER IntegrationKey Integration Key to retrieve .EXAMPLE Get-DuoIntegrations .LINK https://duo.com/docs/adminapi#retrieve-integrations .LINK https://duo.com/docs/adminapi#retrieve-integration-by-integration-key #> [CmdletBinding()] Param( [Parameter()] [string]$IntegrationKey ) if ($IntegrationKey) { $Path = '/admin/v1/integrations/{0}' -f $IntegrationKey } else { $Path = '/admin/v1/integrations' } $DuoRequest = @{ Method = 'GET' Path = $Path } $Response = Invoke-DuoRequest @DuoRequest if ($Response.stat -eq 'OK') { $Response.response } else { $Response } } Set-Alias -Name Get-DuoIntegration -Value Get-DuoIntegrations #EndRegion './Public/Admin API/Integrations/Get-DuoIntegrations.ps1' 48 #Region './Public/Admin API/Integrations/New-DuoIntegration.ps1' 0 function New-DuoIntegration { <# .SYNOPSIS Create Integration .DESCRIPTION Create a new integration. The new integration key and secret key are randomly generated and returned in the response. Requires "Grant applications" API permission. .PARAMETER Name The name of the integration to create. .PARAMETER Type The type of the integration to create. Refer to Retrieve Integrations for a list of valid values. Note that integrations of type "azure-ca" (Microsoft Azure Active Directory) and all Duo Single Sign-On applications may not be created via the API. If an integration has reached the Duo end of support then new instances of that integration may not be created with the API. .PARAMETER AdminApiAdmins Set to 1 to grant an Admin API integration permission for all Admins methods. Default: 0 .PARAMETER AdminApiInfo If creating an Admin API integration, set this to 1 to grant it permission for all Account Info methods. Default: 0. .PARAMETER AdminApiIntegrations Set to 1 to grant an Admin API integration permission for all Integrations methods. Default: 0. .PARAMETER AdminApiReadLog Set to 1 to grant an Admin API integration permission for all Logs methods. Default: 0. .PARAMETER AdminApiReadResource Set to 1 to grant an Admin API integration permission to retrieve objects like users, phones, and hardware tokens. Default: 0. .PARAMETER AdminApiSettings Set to 1 to grant an Admin API integration permission for all Settings methods. Default: 0. .PARAMETER AdminApiWriteResource Set to 1 to grant an Admin API integration permission to create and modify objects like users, phones, and hardware tokens. Default: 0. .PARAMETER Greeting Voice greeting read before the authentication instructions to users who authenticate with a phone callback. .PARAMETER GroupsAllowed A comma-separated list of group IDs that are allowed to authenticate with the integration. If empty, all groups are allowed. Object limits: 100 groups per integration. .PARAMETER NetworksForApiAccess A comma-separated list of IP addresses, IP ranges, or CIDRs specifying the networks allowed to access this API integration. Only applicable to Accounts API and Admin API integrations. A given API integration may apply a network restriction to itself via API; use a different API integration to apply the network restriction, or edit the API application in the Duo Admin Panel GUI. .PARAMETER Notes Description of the integration. .PARAMETER SelfServiceAllowed Set to 1to grant an integration permission to allow users to manage their own devices. This is only supported by integrations which allow for self service configuration. .PARAMETER UsernameNormalizationPolicy Policy for whether or not usernames should be altered before trying to match them to a user account. Refer to Retrieve Integrations for a list of valid values. .EXAMPLE New-DuoIntegration -Type adminapi -AdminApiReadLog 1 -Name 'SIEM logging' -Notes 'This integration is for ingesting logs for SIEM' .LINK https://duo.com/docs/adminapi#create-integration #> [CmdletBinding(SupportsShouldProcess)] Param( [Parameter(Mandatory = $true)] [string]$Name, [Parameter(Mandatory = $true)] [string]$Type, [Parameter()] [ValidateRange(0, 1)] [int]$AdminApiAdmins, [Parameter()] [ValidateRange(0, 1)] [int]$AdminApiInfo, [Parameter()] [ValidateRange(0, 1)] [int]$AdminApiIntegrations, [Parameter()] [ValidateRange(0, 1)] [int]$AdminApiReadLog, [Parameter()] [ValidateRange(0, 1)] [int]$AdminApiReadResource, [Parameter()] [ValidateRange(0, 1)] [int]$AdminApiSettings, [Parameter()] [ValidateRange(0, 1)] [int]$AdminApiWriteResource, [Parameter()] [string]$Greeting, [Parameter()] [string[]]$GroupsAllowed, [Parameter()] [string[]]$NetworksForApiAccess, [Parameter()] [string]$Notes, [Parameter()] [ValidateRange(0, 1)] [int]$SelfServiceAllowed, [Parameter()] [ValidateSet('None', 'Simple')] [string]$UsernameNormalizationPolicy ) $Params = @{ name = $Name type = $Type } if ($Type -eq 'adminapi') { if ($AdminApiAdmins) { $Params.adminapi_admins = $AdminApiAdmins } if ($AdminApiInfo) { $Params.adminapi_info = $AdminApiInfo } if ($AdminApiIntegrations) { $Params.adminapi_integrations = $AdminApiIntegrations } if ($AdminApiReadLog) { $Params.adminapi_read_log = $AdminApiReadLog } if ($AdminApiReadResource) { $Params.adminapi_read_resource = $AdminApiReadResource } if ($AdminApiWriteResource) { $Params.adminapi_write_resource = $AdminApiWriteResource } if ($AdminApiSettings) { $Params.adminapi_settings = $AdminApiSettings } if ($NetworksForApiAccess) { $Params.networks_for_api_access = ($NetworksForApiAccess -join ',') } } elseif ($Type -eq 'accountsapi') { if ($NetworksForApiAccess) { $Params.networks_for_api_access = ($NetworksForApiAccess -join ',') } } if ($Greeting) { $Params.greeting = $Greeting } if ($GroupsAllowed) { $Params.groups_allowed = ($GroupsAllowed -join ',') } if ($Notes) { $Params.notes = $Notes } if ($UsernameNormalizationPolicy) { $Params.username_normalization_policy = $UsernameNormalizationPolicy } $DuoRequest = @{ Method = 'POST' Path = '/admin/v1/integrations' Params = $Params } if ($PSCmdlet.ShouldProcess($Name)) { $Request = Invoke-DuoRequest @DuoRequest if ($Request.stat -ne 'OK') { $Request } else { $Request.response } } } #EndRegion './Public/Admin API/Integrations/New-DuoIntegration.ps1' 160 #Region './Public/Admin API/Integrations/Remove-DuoIntegration.ps1' 0 function Remove-DuoIntegration { <# .SYNOPSIS Delete Integration .DESCRIPTION Delete the integration with integration_key from the system. Attempting to delete the Admin API integration whose secret key is used to sign this request will return an error. Requires "Grant applications" API permission. .PARAMETER IntegrationKey Integration key to remove .EXAMPLE Remove-DuoIntegration -IntegrationKey SOMEDUOKEY .LINK https://duo.com/docs/adminapi#delete-integration .NOTES WARNING: Deleting an integration from Duo can block user logins! Be sure to remove Duo authentication from your product's configuration before you delete the corresponding integration. Depending on the application this could mean uninstalling Duo software from your systems, or updating your device or application settings to no longer include Duo in the authentication process. There is no way to restore an integration deleted in error with Admin API. #> [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')] Param( [Parameter(ValueFromPipelineByPropertyName = $true, Mandatory = $true)] [Alias('integration_key')] [string]$IntegrationKey ) process { $DuoRequest = @{ method = 'DELETE' path = '/admin/v1/integrations/{0}' -f $IntegrationKey } if ($PSCmdlet.ShouldProcess($IntegrationKey)) { Invoke-DuoRequest @DuoRequest } } } #EndRegion './Public/Admin API/Integrations/Remove-DuoIntegration.ps1' 43 #Region './Public/Admin API/Integrations/Update-DuoIntegration.ps1' 0 function Update-DuoIntegration { <# .SYNOPSIS Modify Integration .DESCRIPTION Change the name, enrollment policy, greeting, and/or notes of the integration with integration key integration_key, or reset its secret key. When modifying an Admin API integration permissions can also be added or removed. Requires "Grant applications" API permission. .PARAMETER IntegrationKey Integration key to update .PARAMETER Name New name for the integration. .PARAMETER NetworksForApiAccess A comma-separated list of IP addresses, IP ranges, or CIDRs specifying the networks allowed to access this API integration. Only applicable to Accounts API and Admin API integrations. A given API integration may apply a network restriction to itself via API; use a different API integration to apply the network restriction, or edit the API application in the Duo Admin Panel GUI. .PARAMETER AdminApiAdmins Set to 1 to grant an Admin API integration permission for all Admins methods or 0 to remove access to them. .PARAMETER AdminApiInfo Set to 1 to grant an Admin API integration permission for all Account Info methods or 0 to remove access to them. .PARAMETER AdminApiIntegrations Set to 1 to grant an Admin API integration permission for all Integrations methods or 0 to remove access to them. .PARAMETER AdminApiReadLog Set to 1 to grant an Admin API integration permission for all Logs methods or 0 to remove access to them. .PARAMETER AdminApiReadResource Set to 1 to grant an Admin API integration permission to retrieve objects like users, phones, and hardware tokens or 0 to remove access to them. .PARAMETER AdminApiSettings Set to 1 to grant an Admin API integration permission for all Settings methods or 0 to remove access to them. .PARAMETER AdminApiWriteResource Set to 1to grant an Admin API integration permission to create and modify objects like users, phones, and hardware tokens or 0 to remove access to them. .PARAMETER Greeting New voice greeting. Will be read before the authentication instructions to users who authenticate with a phone callback. .PARAMETER GroupsAllowed A comma-separated list of group IDs that are allowed to authenticate with the integration. If set to an empty string, all groups will be allowed. Object limits: 100 groups per integration. .PARAMETER Notes New description of the integration. .PARAMETER PolicyKey Specify the "Policy Key" value for a custom policy to attach it to the specified integration. Obtain a policy's key by viewing it in the Duo Admin Panel's "Policies" page or from the output of Retrieve Integrations. Leave the value blank to detach the currently attached policy from an integration. .PARAMETER PromptV4Enabled Set to 1 to activate Duo Universal Prompt for the application, or to 0 to revert back to traditional prompt. Only appears for a given integration when frameless_auth_prompt_enabled is 1 (value set automatically when Duo detects a frameless authentication for the integration). .PARAMETER ResetSecretKey If set to 1, resets the integration's secret key to a new, randomly generated value. The new secret key is returned in the return value. Attempting to reset the secret key for the same Admin API integration whose integration key and secret key are used to make this call will return an error. .PARAMETER SelfServiceAllowed Set to 1 to grant an integration permission to allow users to manage their own devices. This is only supported by integrations which allow for self service configuration. .PARAMETER UsernameNormalizationPolicy New policy for whether or not usernames should be altered before trying to match them to a user account. Refer to Retrieve Integrations for a list of valid values. .EXAMPLE Update-DuoIntegration -IntegrationKey SOMEDUOKEY -Greeting 'Welcome to Duo.' .LINK https://duo.com/docs/adminapi#modify-integration .NOTES General notes #> [CmdletBinding(SupportsShouldProcess)] Param( [Parameter(Mandatory = $true)] [string]$IntegrationKey, [Parameter()] [string]$Name, [Parameter()] [ValidateRange(0, 1)] [int]$AdminApiAdmins, [Parameter()] [ValidateRange(0, 1)] [int]$AdminApiInfo, [Parameter()] [ValidateRange(0, 1)] [int]$AdminApiIntegrations, [Parameter()] [ValidateRange(0, 1)] [int]$AdminApiReadLog, [Parameter()] [ValidateRange(0, 1)] [int]$AdminApiReadResource, [Parameter()] [ValidateRange(0, 1)] [int]$AdminApiSettings, [Parameter()] [ValidateRange(0, 1)] [int]$AdminApiWriteResource, [Parameter()] [string]$Greeting, [Parameter()] [string[]]$GroupsAllowed, [Parameter()] [string[]]$NetworksForApiAccess, [Parameter()] [string]$Notes, [Parameter()] [string]$PolicyKey, [Parameter()] [ValidateRange(0, 1)] [int]$PromptV4Enabled, [Parameter()] [ValidateRange(0, 1)] [int]$ResetSecretKey, [Parameter()] [ValidateRange(0, 1)] [int]$SelfServiceAllowed, [Parameter()] [ValidateSet('None', 'Simple')] [string]$UsernameNormalizationPolicy ) $Params = @{} if ($Name) { $Params.name = $Name } if ($AdminApiAdmins) { $Params.adminapi_admins = $AdminApiAdmins } if ($AdminApiInfo) { $Params.adminapi_info = $AdminApiInfo } if ($AdminApiIntegrations) { $Params.adminapi_integrations = $AdminApiIntegrations } if ($AdminApiReadLog) { $Params.adminapi_read_log = $AdminApiReadLog } if ($AdminApiReadResource) { $Params.adminapi_read_resource = $AdminApiReadResource } if ($AdminApiWriteResource) { $Params.adminapi_write_resource = $AdminApiWriteResource } if ($AdminApiSettings) { $Params.adminapi_settings = $AdminApiSettings } if ($NetworksForApiAccess) { $Params.networks_for_api_access = ($NetworksForApiAccess -join ',') } if ($Greeting) { $Params.greeting = $Greeting } if ($GroupsAllowed) { $Params.groups_allowed = ($GroupsAllowed -join ',') } if ($Notes) { $Params.notes = $Notes } if ($PolicyKey) { $Params.policy_key = $PolicyKey } if ($PromptV4Enabled) { $Params.prompt_v4_enabled = $PromptV4Enabled } if ($ResetSecretKey) { $Params.reset_secret_key = $ResetSecretKey } if ($UsernameNormalizationPolicy) { $Params.username_normalization_policy = $UsernameNormalizationPolicy } $DuoRequest = @{ Method = 'POST' Path = '/admin/v1/integrations/{0}' -f $IntegrationKey Params = $Params } if ($PSCmdlet.ShouldProcess($IntegrationKey)) { $Request = Invoke-DuoRequest @DuoRequest if ($Request.stat -ne 'OK') { $Request } else { $Request.response } } } #EndRegion './Public/Admin API/Integrations/Update-DuoIntegration.ps1' 175 #Region './Public/Admin API/Custom Branding/Add-DuoCustomBrandingDraftUser.ps1' 0 function Add-DuoCustomBrandingDraftUser { <# .SYNOPSIS Add Draft Custom Branding User by ID .DESCRIPTION Add a single user with ID user_id to the list of draft branding test users. Requires "Grant settings" API permission. .EXAMPLE Add-DuoCustomBrandingDraftUser -UserId SOMEUSERID .INPUTS None .OUTPUTS PSCustomObject. Returns a Duo Response object. .LINK https://duo.com/docs/adminapi#add-draft-custom-branding-user-by-id .NOTES #> [CmdletBinding(SupportsShouldProcess)] Param( [Parameter(ValueFromPipelineByPropertyName = $true, Mandatory = $true)] [Alias('user_id')] [string]$UserId ) $DuoRequest = @{ Method = 'POST' Path = '/admin/v1/branding/draft/users/{0}' -f $UserId } if ($PSCmdlet.ShouldProcess($UserId)) { $Request = Invoke-DuoRequest @DuoRequest if ($Request.stat -ne 'OK') { $Request } else { $Request.response } } } #EndRegion './Public/Admin API/Custom Branding/Add-DuoCustomBrandingDraftUser.ps1' 45 #Region './Public/Admin API/Custom Branding/Get-DuoCustomBranding.ps1' 0 function Get-DuoCustomBranding { <# .SYNOPSIS Retrieve Draft Custom Branding .DESCRIPTION Returns custom branding settings. These settings can also be viewed and set in the Duo Admin Panel. Requires "Grant settings" API permission. .PARAMETER Draft Use this switch to retreieve the draft branding instead of live. .PARAMETER OutputDirectory Path to save the branding images to. If the directory does not exist, it will be created. .EXAMPLE Get-DuoCustomBranding .INPUTS None .OUTPUTS PSCustomObject. Returns a Duo Response object. .LINK https://duo.com/docs/adminapi#retrieve-live-custom-branding .LINK https://duo.com/docs/adminapi#retrieve-draft-custom-branding .NOTES This commandlet supports both Draft and Live branding options. #> [CmdletBinding()] Param( [Parameter()] [switch]$Draft, [Parameter()] [string]$OutputDirectory ) if ($Draft) { $Path = '/admin/v1/branding/draft' } else { $Path = '/admin/v1/branding' } $DuoRequest = @{ Method = 'GET' Path = $Path } $Request = Invoke-DuoRequest @DuoRequest if ($Request.stat -ne 'OK') { $Request if ($OutputDirectory) { if (!Test-Path $OutputDirectory) { New-Item -ItemType Directory $OutputDirectory | Out-Null } if ($Request.background_image) { $ImageFile = Join-Path $OutputDirectory '/background_image.png' ConvertFrom-Base64 -Base64 $Request.background_image -Path $ImageFile } if ($Request.logo) { $ImageFile = Join-Path $OutputDirectory '/logo.png' ConvertFrom-Base64 -Base64 $Request.logo -Path $ImageFile } } } else { $Request.response } } #EndRegion './Public/Admin API/Custom Branding/Get-DuoCustomBranding.ps1' 75 #Region './Public/Admin API/Custom Branding/Get-DuoCustomMessaging.ps1' 0 function Get-DuoCustomMessaging { <# .SYNOPSIS Retrieve Custom Messaging .DESCRIPTION Returns effective custom messaging settings, shown to users in the Universal Prompt. These settings can also be viewed and set in the Duo Admin Panel. Supersedes the helpdesk_message Settings parameter. Requires "Grant settings" API permission. .EXAMPLE Get-DuoCustomMessaging .INPUTS None .OUTPUTS PSCustomObject. Returns a Duo Custom Messaging object. .LINK https://duo.com/docs/adminapi#retrieve-custom-messaging .NOTES #> [CmdletBinding()] Param() $DuoRequest = @{ Method = 'GET' Path = '/admin/v1/branding/custom_messaging' } $Request = Invoke-DuoRequest @DuoRequest if ($Request.stat -ne 'OK') { $Request } else { $Request.response } } #EndRegion './Public/Admin API/Custom Branding/Get-DuoCustomMessaging.ps1' 39 #Region './Public/Admin API/Custom Branding/Publish-DuoCustomBranding.ps1' 0 function Publish-DuoCustomBranding { <# .SYNOPSIS Publish Draft Custom Branding as Live Custom Branding .DESCRIPTION Replaces the current live custom branding with the draft custom branding for all users and then removes the draft branding. Requires "Grant settings" API permission. .EXAMPLE Publish-DuoCustomBranding .INPUTS None .OUTPUTS PSCustomObject. Returns a Duo Response object. .LINK https://duo.com/docs/adminapi#publish-draft-custom-branding-as-live-custom-branding .NOTES #> [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')] Param() $DuoRequest = @{ Method = 'POST' Path = '/admin/v1/branding/draft/publish' } if ($PSCmdlet.ShouldProcess()) { $Request = Invoke-DuoRequest @DuoRequest if ($Request.stat -ne 'OK') { $Request } else { $Request.response } } } #EndRegion './Public/Admin API/Custom Branding/Publish-DuoCustomBranding.ps1' 41 #Region './Public/Admin API/Custom Branding/Remove-DuoCustomBrandingDraftUser.ps1' 0 function Remove-DuoCustomBrandingDraftUser { <# .SYNOPSIS Remove Draft Custom Branding User by ID .DESCRIPTION Remove a single user with ID user_id from the list of draft branding test users. Requires "Grant settings" API permission. .EXAMPLE Remove-DuoCustomBrandingDraftUser -UserId SOMEUSERID .INPUTS None .OUTPUTS PSCustomObject. Returns a Duo Response object. .LINK https://duo.com/docs/adminapi#add-draft-custom-branding-user-by-id .NOTES #> [CmdletBinding(SupportsShouldProcess)] Param( [Parameter(ValueFromPipelineByPropertyName = $true, Mandatory = $true)] [Alias('user_id')] [string]$UserId ) $DuoRequest = @{ Method = 'DELETE' Path = '/admin/v1/branding/draft/users/{0}' -f $UserId } if ($PSCmdlet.ShouldProcess($UserId)) { $Request = Invoke-DuoRequest @DuoRequest if ($Request.stat -ne 'OK') { $Request } else { $Request.response } } } #EndRegion './Public/Admin API/Custom Branding/Remove-DuoCustomBrandingDraftUser.ps1' 45 #Region './Public/Admin API/Custom Branding/Set-DuoCustomBranding.ps1' 0 function Set-DuoCustomBranding { <# .SYNOPSIS Modify Custom Branding .DESCRIPTION Change effective or draft custom branding settings. These settings can also be viewed and set in the Duo Admin Panel. Requires "Grant settings" API permission. .PARAMETER Draft Use this switch to modify the draft branding instead of live. .PARAMETER BackgroundImg A PNG file path or base64 encoded background image in PNG format, with maximum size less than 3MB and dimensions between 12 by 12 pixels and 3840 by 2160 pixels. Shown in Duo SSO and Duo Universal Prompt. .PARAMETER CardAccentColor A CSS hex color shown as the hash symbol (#) followed by three or six hexadecimal digits, which represents the colored line appearing at the top of the interactive user interface. Shown in Duo SSO and Universal Prompt. .PARAMETER Logo A PNG file path or base64 encoded logo image in PNG format, with maximum size less than 200KB and dimensions between 12 by 12 pixels and 500 by 500 pixels. Shown in Duo SSO, Duo Universal Prompt, and traditional prompt. .PARAMETER PageBackgroundColor A CSS hex color shown as the hash symbol (#) followed by three or six hexadecimal digits, which represents the color appearing behind the user interface and any transparent background image. Shown in Duo SSO and Universal Prompt. .PARAMETER PoweredByDuo If true, Duo SSO, Duo Universal Prompt, and traditional prompt show the "Secured by Duo" branding. Otherwise, false. .PARAMETER UserIds A comma separated list of user IDs that will see saved draft branding in Duo SSO and Duo Universal Prompt. .EXAMPLE Set-DuoCustomBranding -Draft -Logo c:\path\to\logo.png .INPUTS None .OUTPUTS PSCustomObject. Returns a Duo Response object. .LINK https://duo.com/docs/adminapi#modify-live-custom-branding .LINK https://duo.com/docs/adminapi#modify-draft-custom-branding .NOTES This commandlet supports both Draft and Live branding options. #> [CmdletBinding(SupportsShouldProcess)] Param( [Parameter()] [switch]$Draft, [Parameter()] [string]$BackgroundImg, [Parameter()] [ValidatePattern('^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$')] [string]$CardAccentColor, [Parameter()] [string]$Logo, [Parameter()] [ValidatePattern('^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$')] [string]$PageBackgroundColor, [Parameter()] [switch]$PoweredByDuo, [Parameter()] [string[]]$UserIds ) process { if ($Draft) { $Path = '/admin/v1/branding/draft' } else { $Path = '/admin/v1/branding' } $Params = @{} if ($BackgroundImg) { if (Test-Path $BackgroundImg) { if (Test-PngFile -Path $BackgroundImg) { $BackgroundImg = ConvertTo-Base64 -Path $BackgroundImg } else { Write-Error "$BackgroundImg is not a PNG file" return $false } } $Params.background_img = $BackgroundImg } if ($CardAccentColor) { $Params.card_accent_color = $CardAccentColor } if ($Logo) { if (Test-Path $Logo) { if (Test-PngFile -Path $Logo) { $Logo = ConvertTo-Base64 -Path $Logo } else { Write-Error "$Logo is not a PNG file" return $false } } $Params.background_img = $BackgroundImg } if ($PageBackgroundColor) { $Params.page_background_color = $PageBackgroundColor } if ($PoweredByDuo.IsPresent) { $Params.powered_by_duo = $PoweredByDuo.IsPresent } if ($Draft) { if ($UserIds) { $Params.user_ids = @($UserIds) } } $DuoRequest = @{ Method = 'POST' Path = $Path Parameters = $Params } if ($PSCmdlet.ShouldProcess($AccountId)) { $Response = Invoke-DuoRequest @DuoRequest if ($Response.stat -eq 'OK') { $Response.response } else { $Response } } } } #EndRegion './Public/Admin API/Custom Branding/Set-DuoCustomBranding.ps1' 136 #Region './Public/Admin API/Custom Branding/Set-DuoCustomMessaging.ps1' 0 function Set-DuoCustomBranding { <# .SYNOPSIS Modify Custom Messaging .DESCRIPTION Updates current custom messaging settings, shown to users in the Universal Prompt. These settings can also be viewed and set in the Duo Admin Panel. Supersedes the helpdesk_message Settings parameter. Requires "Grant settings" API permission. .PARAMETER HelpLinks A comma-separated list of up to two custom external links shown to users in the Universal Prompt. Each URL must begin with http:// or https:// .PARAMETER HelpText Customized text string associated with the specified locale. The user's browser's preferred language settings determine which language to show in the Universal Prompt. The first locale and message text in the list matches the default language specified in global Settings and is also shown in the traditional web prompt and in the Duo Device Health app. Up to 200 characters. No support for hyperlinks. .PARAMETER Locale The language of the help text. One of: en_US (English), cs_CZ (Czech), de_DE (German), es_ES (Spanish - Spain), es_419 (Spanish - Latin America), fi_FI (Finnish), fr_FR (French), hi_IN (Hindi), id_ID (Indonesian), it_IT (Italian), ja_JP (Japanese), ko_KR (Korean), nb_NO (Norwegian - Bokmal), pl_PL (Polish), pt_BR (Portuguese - Brazil), sv_SE (Swedish), th_TH (Thai), tr_TR (Turkish), vi_VN (Vietnamese), or zh_hans_CN (Chinese - Simplified). .EXAMPLE Set-DuoCustomMessaging -HelpLinks 'https://duo.com/docs/adminapi#modify-custom-messaging' .INPUTS None .OUTPUTS PSCustomObject. Returns a Duo Custom Messaing object. .LINK https://duo.com/docs/adminapi#modify-custom-messaging .NOTES #> [CmdletBinding(SupportsShouldProcess, DefaultParameterSetName = 'NoHelpText')] Param( [Parameter()] [string[]]$HelpLinks, [Parameter(Mandatory = $true, ParameterSetName = 'HelpText')] [string]$HelpText, [Parameter(ParameterSetName = 'HelpText')] [ValidateSet('en_US', 'cs_CZ', 'es_419', 'fi_FI', 'fr_FR', 'hi_IN', 'id_ID', 'it_IT', 'ja_JP', 'ko_KR', 'nb_NO', 'pl_PL', 'pt_BR', 'sv_SE', 'th_TH', 'tr_TR', 'vi_VN', 'zh_hans_CN')] [string]$Locale = 'en_US' ) process { $Params = @{} if ($HelpLinks) { $HelpLinks = $HelpLinks | Select-Object -First 2 $Params.help_links = $HelpLinks -join ',' } if ($HelpText) { $Params.help_text = $HelpText $Params.locale = $Locale } $DuoRequest = @{ Method = 'POST' Path = '/admin/v1/branding/custom_messaging' Parameters = $Params } if ($PSCmdlet.ShouldProcess($AccountId)) { $Response = Invoke-DuoRequest @DuoRequest if ($Response.stat -eq 'OK') { $Response.response } else { $Response } } } } #EndRegion './Public/Admin API/Custom Branding/Set-DuoCustomMessaging.ps1' 75 #Region './Public/Admin API/WebAuthn/Get-DuoWebAuthnCredentials.ps1' 0 function Get-DuoWebAuthnCredentials { <# .SYNOPSIS Retrieve WebAuthn Credentials by Key .DESCRIPTION Return the single WebAuthn credential with webauthnkey. Requires "Grant read resource" API permission. .PARAMETER WebAuthnKey WebAuthn Key .EXAMPLE Get-DuoWebAuthnCredentials .LINK https://duo.com/docs/adminapi#retrieve-webauthn-credentials .LINK https://duo.com/docs/adminapi#retrieve-webauthn-credentials-by-key #> [CmdletBinding(DefaultParameterSetName = 'List')] Param( [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'Single')] [string]$WebAuthnKey ) process { $DuoRequest = @{ Method = 'GET' Params = $Params } switch ($PSCmdlet.ParameterSetName) { 'Single' { $DuoRequest.Path = '/admin/v1/webauthncredentials/{0}' -f $WebAuthnKey $Request = Invoke-DuoRequest @DuoRequest if ($Request.stat -ne 'OK') { $Request } else { $Request.response } } 'List' { $DuoRequest.Path = '/admin/v1/webauthncredentials' Invoke-DuoPaginatedRequest -DuoRequest $DuoRequest } } } } Set-Alias -Name Get-DuoWebAuthnCredential -Value Get-DuoTokens #EndRegion './Public/Admin API/WebAuthn/Get-DuoWebAuthnCredentials.ps1' 53 #Region './Public/Admin API/WebAuthn/Remove-DuoWebAuthnCredential.ps1' 0 function Remove-DuoWebAuthnCredential { <# .SYNOPSIS Delete WebAuthn Credential .DESCRIPTION Delete the WebAuthn credential with key webauthnkey from the system. Requires "Grant write resource" API permission. .PARAMETER WebAuthnKey WebAuthn Key .EXAMPLE Remove-DuoWebAuthnCredential -WebAuthnKey SOMEWEBAUTHNKEY .LINK https://duo.com/docs/adminapi#delete-webauthn-credential #> [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')] Param( [Parameter(ValueFromPipelineByPropertyName = $true, Mandatory = $true)] [string]$WebAuthnKey ) process { $DuoRequest = @{ method = 'DELETE' path = '/admin/v1/webauthncredentials/{0}' -f $WebAuthnKey } if ($PSCmdlet.ShouldProcess($WebAuthnKey)) { Invoke-DuoRequest @DuoRequest } } } #EndRegion './Public/Admin API/WebAuthn/Remove-DuoWebAuthnCredential.ps1' 34 #Region './Public/Admin API/Endpoints/Get-DuoEndpoints.ps1' 0 function Get-DuoEndpoints { <# .SYNOPSIS Retrieve Endpoints .DESCRIPTION Returns a single endpoint or a paged list of endpoints. Requires "Grant read resource" API permission. Information for a given endpoint is purged after 30 days of inactivity. Endpoint information retrievable by Duo Beyond and Duo Access customers. In addition, some response information is available only with Duo Beyond. .PARAMETER EndpointKey The key for the endpoint .EXAMPLE Get-DuoEndpoints .LINK https://duo.com/docs/adminapi#retrieve-endpoints .LINK https://duo.com/docs/adminapi#retrieve-endpoint-by-id #> [CmdletBinding()] Param( [Parameter(ValueFromPipelineByPropertyName = $true)] [Alias('epkey')] [string]$EndpointKey ) process { if ($BypassCodeId) { $Path = '/admin/v1/bypass_codes/{0}' -f $BypassCodeId } else { $Path = '/admin/v1/bypass_codes' } $DuoRequest = @{ Method = 'GET' Path = $Path } if ($EndpointKey) { $Request = Invoke-DuoRequest @DuoRequest if ($Request.stat -ne 'OK') { $Request } else { $Request.response } } else { Invoke-DuoPaginatedRequest -DuoRequest $DuoRequest } } } Set-Alias -Name Get-DuoEndpoint -Value Get-DuoEndpoints #EndRegion './Public/Admin API/Endpoints/Get-DuoEndpoints.ps1' 59 #Region './Public/Admin API/Tokens/Get-DuoTokens.ps1' 0 function Get-DuoTokens { <# .SYNOPSIS Retrieve Hardware Tokens .DESCRIPTION Returns a single hardware token or a paged list of OTP hardware tokens. If no type and serial parameters are provided, the list will contain all hardware tokens. Otherwise, the list will contain either a single hardware token (if a match was found) or no hardware tokens. Requires "Grant read resource" API permission. .PARAMETER TokenId Id of token .PARAMETER Type Specify a type and serial number to look up a single hardware token. One of: Type Description "t6" TOTP-6 hardware token "t8" TOTP-8 hardware token "h6" HOTP-6 hardware token "h8" HOTP-8 hardware token "yk" YubiKey AES hardware token "d1" Duo-D100 hardware token * This option is required if serial is present. .PARAMETER Serial The serial number of the hardware token. * This option is required if type is present. .EXAMPLE Get-DuoTokens .LINK https://duo.com/docs/adminapi#retrieve-hardware-tokens .LINK https://duo.com/docs/adminapi#retrieve-hardware-token-by-id #> [CmdletBinding(DefaultParameterSetName = 'List')] Param( [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'Single')] [Alias('token_id')] [string]$TokenId, [Parameter(ParameterSetName = 'List')] [ValidateSet('h6', 'h8', 't6', 't8', 'yk', 'd1')] [string]$Type, [Parameter(ParameterSetName = 'List')] [string]$Serial ) process { switch ($PSCmdlet.ParameterSetName) { 'Single' { $Path = '/admin/v1/tokens/{0}' -f $TokenId } 'List' { $Path = '/admin/v1/tokens' } } $Params = @{} if ($Type) { $Params.type = $Type } if ($Serial) { $Params.serial = $Serial } $DuoRequest = @{ Method = 'GET' Path = $Path Params = $Params } switch ($PSCmdlet.ParameterSetName) { 'Single' { $Request = Invoke-DuoRequest @DuoRequest if ($Request.stat -ne 'OK') { $Request } else { $Request.response } } default { Invoke-DuoPaginatedRequest -DuoRequest $DuoRequest } } } } Set-Alias -Name Get-DuoToken -Value Get-DuoTokens #EndRegion './Public/Admin API/Tokens/Get-DuoTokens.ps1' 89 #Region './Public/Admin API/Tokens/New-DuoToken.ps1' 0 function New-DuoToken { <# .SYNOPSIS Create Hardware Token .DESCRIPTION Create a new hardware token. Requires "Grant write resource" API permission. .PARAMETER Type The type of hardware token to import. One of: Type Description ---- ----------- "t6" TOTP-6 hardware token "t8" TOTP-8 hardware token "h6" HOTP-6 hardware token "h8" HOTP-8 hardware token "yk" YubiKey AES hardware token Duo-D100 tokens (type "d1") are imported when purchased from Duo and may not be created via the Admin API. .PARAMETER Serial The serial number of the token (maximum length 128 characters). .PARAMETER Secret The TOTP/HOTP secret. This parameter is required for TOTP-6, TOTP-8, HOTP-6 and HOTP-8 hardware tokens. .PARAMETER Counter Initial value for the HOTP counter. This parameter is only valid for TOTP-6, TOTP-8, HOTP-6 and HOTP-8 hardware tokens. Default: 0. .PARAMETER PrivateId The 12-character YubiKey private ID. This parameter is required for YubiKey hardware tokens. .PARAMETER AesKey The 32-character YubiKey AES key. This parameter is required for YubiKey hardware tokens. .EXAMPLE $Secret = New-DuoTokenTotpSecret New-DuoToken -Serial 001 -Type t6 -Secret $Secret.Hex .LINK https://duo.com/docs/adminapi#create-hardware-token .NOTES See New-DuoTokenTotpSecret for more info about generating TOTP secrets #> [CmdletBinding(SupportsShouldProcess)] Param( [Parameter(Mandatory = $true)] [ValidateSet('h6', 'h8', 't6', 't8', 'yk', 'd1')] [string]$Type, [Parameter(Mandatory = $true)] [string]$Serial, [Parameter()] [string]$Secret, [Parameter()] [int]$Counter, [Parameter()] [string]$PrivateId, [Parameter()] [string]$AesKey ) $Params = @{ type = $Type serial = $Serial } if ($Type -in @('h6', 'h8', 't6', 't8')) { if (!$Secret) { $Secret = Read-Host 'OTP secret' -MaskInput } $Params.secret = $Secret if ($Counter) { $Params.counter = $Counter } } elseif ($Type -eq 'yk') { if (!$PrivateId) { $PrivateId = Read-Host 'YubiKey Private ID' -MaskInput } if (!$AesKey) { $AesKey = Read-Host 'YubiKey AES Key' -MaskInput } $Params.private_id = $PrivateId $Params.aes_key = $AesKey } $DuoRequest = @{ Method = 'POST' Path = '/admin/v1/tokens' Params = $Params } if ($PSCmdlet.ShouldProcess($Type)) { $Request = Invoke-DuoRequest @DuoRequest if ($Request.stat -ne 'OK') { $Request } else { $Request.response } } } #EndRegion './Public/Admin API/Tokens/New-DuoToken.ps1' 110 #Region './Public/Admin API/Tokens/Remove-DuoToken.ps1' 0 function Remove-DuoToken { <# .SYNOPSIS Delete Hardware Token .DESCRIPTION Delete the hardware token with ID token_id from the system. Requires "Grant write resource" API permission. .PARAMETER TokenId Id of token .EXAMPLE Remove-DuoToken -TokenId SOMEDUOID .LINK https://duo.com/docs/adminapi#delete-hardware-token #> [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')] Param( [Parameter(ValueFromPipelineByPropertyName = $true, Mandatory = $true)] [Alias('token_id')] [string]$TokenId ) process { $DuoRequest = @{ method = 'DELETE' path = '/admin/v1/tokens/{0}' -f $TokenId } if ($PSCmdlet.ShouldProcess($TokenId)) { Invoke-DuoRequest @DuoRequest } } } #EndRegion './Public/Admin API/Tokens/Remove-DuoToken.ps1' 36 #Region './Public/Admin API/Tokens/Sync-DuoToken.ps1' 0 function Sync-DuoToken { <# .SYNOPSIS Resync Hardware Token .DESCRIPTION Resynchronize the hardware token with ID token_id by providing three successive codes from the token. Only HOTP and Duo-D100 tokens can be resynchronized. YubiKey tokens operating in their native AES mode do not need resynchronization. Requires "Grant write resource" API permission. .PARAMETER TokenId Id of token .PARAMETER Code1 The first code from the token. .PARAMETER Code2 The second code from the token. .PARAMETER Code3 The third code from the token. .EXAMPLE Sync-DuoToken -TokenId SOMEDUOID -Code1 123456 -Code2 789012 -Code3 345678 .LINK https://duo.com/docs/adminapi#resync-hardware-token #> [CmdletBinding(SupportsShouldProcess)] Param( [Parameter(ValueFromPipelineByPropertyName = $true, Mandatory = $true)] [Alias('token_id')] [string]$TokenId, [Parameter(Mandatory = $true)] [string]$Code1, [Parameter(Mandatory = $true)] [string]$Code2, [Parameter(Mandatory = $true)] [string]$Code3 ) process { $Params = @{ code1 = $Code1 code2 = $Code2 code3 = $Code3 } $DuoRequest = @{ Method = 'POST' Path = '/admin/v1/tokens/{0}/resync' -f $TokenId Params = $Params } if ($PSCmdlet.ShouldProcess($TokenId)) { $Request = Invoke-DuoRequest @DuoRequest if ($Request.stat -ne 'OK') { $Request } else { $Request.response } } } } #EndRegion './Public/Admin API/Tokens/Sync-DuoToken.ps1' 67 #Region './Public/Admin API/Administrative Units/Add-DuoAdminToAdminUnit.ps1' 0 function Add-DuoAdminToAdminUnit { <# .SYNOPSIS Add Administrator to Administrative Unit .DESCRIPTION Assign the administrator with admin_id to the administrative unit with admin_unit_id. The administrator user must have restricted_by_admin_units set to true before attempting to assign them to an administrative unit via the API. Requires "Grant administrators" API permission. .PARAMETER AdminUnitId The ID of the Administrative Unit .PARAMETER AdminId The ID of the Administrator .EXAMPLE Add-DuoAdminToAdminUnit -AdminUnitId SOMEADMINUNITID -AdminId SOMEADMINID .LINK https://duo.com/docs/adminapi#add-administrator-to-administrative-unit .NOTES Object limits: 100 groups per user. #> [CmdletBinding(SupportsShouldProcess)] Param( [Parameter(ValueFromPipelineByPropertyName = $true, Mandatory = $true)] [Alias('admin_unit_id')] [string]$AdminUnitId, [Parameter(ValueFromPipelineByPropertyName = $true, Mandatory = $true)] [Alias('admin_id')] [string]$AdminId ) process { $DuoRequest = @{ Method = 'POST' Path = '/admin/v1/administrative_units/{0}/admin/{1}' -f $AdminUnitId, $AdminId } $Target = 'Unit: {0} - Admin {1}' -f $AdminUnitId, $AdminId if ($PSCmdlet.ShouldProcess($Target)) { Invoke-DuoRequest @DuoRequest } } } #EndRegion './Public/Admin API/Administrative Units/Add-DuoAdminToAdminUnit.ps1' 47 #Region './Public/Admin API/Administrative Units/Add-DuoGroupToAdminUnit.ps1' 0 function Add-DuoGroupToAdminUnit { <# .SYNOPSIS Add Group to Administrative Unit .DESCRIPTION Assign the group with group_id to the administrative unit with admin_unit_id. Requires "Grant administrators" API permission. .PARAMETER AdminUnitId The ID of the Administrative Unit .PARAMETER GroupId The ID of the Group .EXAMPLE Add-DuoGroupToAdminUnit -AdminUnitId SOMEADMINUNITID -GroupId SOMEGROUPID .LINK https://duo.com/docs/adminapi#add-group-to-administrative-unit .NOTES #> [CmdletBinding(SupportsShouldProcess)] Param( [Parameter(ValueFromPipelineByPropertyName = $true, Mandatory = $true)] [Alias('admin_unit_id')] [string]$AdminUnitId, [Parameter(ValueFromPipelineByPropertyName = $true, Mandatory = $true)] [Alias('group_id')] [string]$GroupId ) process { $DuoRequest = @{ Method = 'POST' Path = '/admin/v1/administrative_units/{0}/group/{1}' -f $AdminUnitId, $GroupId } $Target = 'Unit: {0} - Group {1}' -f $AdminUnitId, $GroupId if ($PSCmdlet.ShouldProcess($Target)) { Invoke-DuoRequest @DuoRequest } } } #EndRegion './Public/Admin API/Administrative Units/Add-DuoGroupToAdminUnit.ps1' 47 #Region './Public/Admin API/Administrative Units/Add-DuoIntegrationToAdminUnit.ps1' 0 function Add-DuoIntegrationToAdminUnit { <# .SYNOPSIS Add Integration to Administrative Unit .DESCRIPTION Assign the integration with integration_key to the administrative unit with admin_unit_id. Requires "Grant administrators" API permission. .PARAMETER AdminUnitId The ID of the Administrative Unit .PARAMETER IntegrationKey The Key of the Integration .EXAMPLE Add-DuoIntegrationToAdminUnit -AdminUnitId SOMEADMINUNITID -IntegrationKey SOMEINTEGRATIONKEY .LINK https://duo.com/docs/adminapi#add-group-to-administrative-unit .NOTES #> [CmdletBinding(SupportsShouldProcess)] Param( [Parameter(ValueFromPipelineByPropertyName = $true, Mandatory = $true)] [Alias('admin_unit_id')] [string]$AdminUnitId, [Parameter(ValueFromPipelineByPropertyName = $true, Mandatory = $true)] [Alias('integration_key')] [string]$IntegrationKey ) process { $DuoRequest = @{ Method = 'POST' Path = '/admin/v1/administrative_units/{0}/integration/{1}' -f $AdminUnitId, $IntegrationKey } $Target = 'Unit: {0} - Integration {1}' -f $AdminUnitId, $IntegrationKey if ($PSCmdlet.ShouldProcess($Target)) { Invoke-DuoRequest @DuoRequest } } } #EndRegion './Public/Admin API/Administrative Units/Add-DuoIntegrationToAdminUnit.ps1' 47 #Region './Public/Admin API/Administrative Units/Get-DuoAdminUnits.ps1' 0 function Get-DuoAdminUnits { <# .SYNOPSIS Retrieve Administrative Units .DESCRIPTION Returns a single administrative unit or a paged list of all administrative units if no parameters specified. Requires "Grant administrators" API permission. Optionally specify at most one parameter to return a list of administrative units associated with the given administrator, group, or integration. .PARAMETER AdminUnitId The ID of the Adminstrative Unit .LINK https://duo.com/docs/adminapi#retrieve-administrative-units .LINK https://duo.com/docs/adminapi#retrieve-administrative-unit-details .EXAMPLE Get-DuoAdminUnits #> [CmdletBinding()] Param( [Parameter(ValueFromPipelineByPropertyName = $true)] [Alias('admin_unit_id')] [string]$AdminUnitId ) process { if ($AdminId) { $Path = '/admin/v1/administrative_units/{0}' -f $AdminId } else { $Path = '/admin/v1/administrative_units' } $DuoRequest = @{ Method = 'GET' Path = $Path } if ($AdminId) { $Request = Invoke-DuoRequest @DuoRequest if ($Request.stat -ne 'OK') { $Request } else { $Request.response } } else { Invoke-DuoPaginatedRequest -DuoRequest $DuoRequest } } } Set-Alias -Name Get-DuoAdministrativeUnit -Value Get-DuoAdmininstrativeUnits #EndRegion './Public/Admin API/Administrative Units/Get-DuoAdminUnits.ps1' 56 #Region './Public/Admin API/Administrative Units/New-DuoAdminUnit.ps1' 0 function New-DuoAdminUnit { <# .SYNOPSIS Add Administrative Unit .DESCRIPTION Add a new administrative unit with specified administrators, groups, or other parameters. Requires "Grant administrators" API permission. .PARAMETER Name The name of the new administrative unit. Must be unique amongst all administrative units. .PARAMETER Description A description of the new administrative unit. .PARAMETER RestrictByGroups Does the new administrative unit specify groups? Default: false. .PARAMETER RestrictByIntegrations Does the new administrative unit specify integrations? Default: false. .PARAMETER Admins One or more admin_id values to assign administrators to the new administrative unit. The administrator user must have restricted_by_admin_units set to true before attempting to assign them to an administrative unit via the API. .PARAMETER Groups One or more group_id values to assign groups to the new administrative unit. .PARAMETER Integrations One or more integration_key values to assign integrations to the new administrative unit. .EXAMPLE New-DuoAdminUnit -Name 'Accounts Payable Admins' -RestrictByGroups -Groups 'ACCTSPAYABLEGROUPID' .LINK https://duo.com/docs/adminapi#add-administrative-unit #> [CmdletBinding(SupportsShouldProcess)] Param( [Parameter(Mandatory = $true)] [string]$Name, [Parameter(Mandatory = $true)] [string]$Description, [Parameter()] [switch]$RestrictByGroups, [Parameter()] [switch]$RestrictByIntegrations, [Parameter()] [string[]]$Admins, [Parameter()] [string[]]$Groups, [Parameter()] [string[]]$Integrations ) $Params = @{ name = $Name description = $Description restrict_by_groups = $RestrictByGroups.IsPresent } if ($Admins) { $Params.admins = $Admins } if ($RestrictByGroups.IsPresent) { if ($Groups) { $Params.groups = $Groups } } if ($RestrictByIntegrations.IsPresent) { $Params.restrict_by_integrations = $RestrictByIntegrations.IsPresent if ($Integrations) { $Params.integrations = $Integrations } } $DuoRequest = @{ Method = 'POST' Path = '/admin/v1/admins' Params = $Params } if ($PSCmdlet.ShouldProcess($Name)) { $Request = Invoke-DuoRequest @DuoRequest if ($Request.stat -ne 'OK') { $Request } else { $Request.response } } } #EndRegion './Public/Admin API/Administrative Units/New-DuoAdminUnit.ps1' 94 #Region './Public/Admin API/Administrative Units/Remove-DuoAdminFromAdminUnit.ps1' 0 function Remove-DuoAdminFromAdminUnit { <# .SYNOPSIS Remove Administrator from Administrative Unit .DESCRIPTION Unassign the administrator with admin_id from the administrative unit with admin_unit_id. The administrator user will still have restricted_by_admin_units set to true, and if the admin is not assigned to any other admin unit they will not be able to view any users or integrations. Be sure to change the value of restricted_by_admin_units to false to permit that admin to view all users and integrations. Requires "Grant administrators" API permission. .PARAMETER AdminUnitId The ID of the Administrative Unit .PARAMETER AdminId The ID of the Administrator .EXAMPLE Remove-DuoAdminFromAdminUnit -AdminUnitId SOMEADMINUNITID -AdminId SOMEADMINID .LINK https://duo.com/docs/adminapi#remove-administrator-from-administrative-unit #> [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')] Param( [Parameter(ValueFromPipelineByPropertyName = $true, Mandatory = $true)] [Alias('admin_unit_id')] [string]$AdminUnitId, [Parameter(ValueFromPipelineByPropertyName = $true, Mandatory = $true)] [Alias('admin_id')] [string]$AdminId ) process { $DuoRequest = @{ method = 'DELETE' path = '/admin/v1/administrative_units/{0}/admin/{1}' -f $AdminUnitId, $AdminId } $Target = 'Unit {0} - Admin {1}' -f $AdminUnitId, $AdminId if ($PSCmdlet.ShouldProcess($Target)) { Invoke-DuoRequest @DuoRequest } } } #EndRegion './Public/Admin API/Administrative Units/Remove-DuoAdminFromAdminUnit.ps1' 44 #Region './Public/Admin API/Administrative Units/Remove-DuoGroup.ps1' 0 function Remove-DuoAdminUnit { <# .SYNOPSIS Delete Administrative Unit .DESCRIPTION Delete the administrative unit with admin_unit_id from the system. Requires "Grant administrators" API permission. .PARAMETER AdminUnitId Admin Unit Id to remove .EXAMPLE Remove-DuoAdminUnit -AdminUnitId SOMEDUOID .LINK https://duo.com/docs/adminapi#delete-administrative-unit #> [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')] Param( [Parameter(ValueFromPipelineByPropertyName = $true, Mandatory = $true)] [Alias('admin_unit_id')] [string]$AdminUnitId ) process { $DuoRequest = @{ method = 'DELETE' path = '/admin/v1/administrative_units/{0}' -f $AdminUnitId } if ($PSCmdlet.ShouldProcess($AdminUnitId)) { Invoke-DuoRequest @DuoRequest } } } #EndRegion './Public/Admin API/Administrative Units/Remove-DuoGroup.ps1' 36 #Region './Public/Admin API/Administrative Units/Remove-DuoGroupFromAdminUnit.ps1' 0 function Remove-DuoGroupFromAdminUnit { <# .SYNOPSIS Remove Group from Administrative Unit .DESCRIPTION Unassign the group with group_id from the administrative unit with admin_unit_id. Requires "Grant administrators" API permission. .PARAMETER AdminUnitId The ID of the Administrative Unit .PARAMETER GroupId The ID of the Group .EXAMPLE Remove-DuoAdminFromAdminUnit -AdminUnitId SOMEADMINUNITID -AdminId SOMEADMINID .LINK https://duo.com/docs/adminapi#remove-group-from-administrative-unit #> [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')] Param( [Parameter(ValueFromPipelineByPropertyName = $true, Mandatory = $true)] [Alias('admin_unit_id')] [string]$AdminUnitId, [Parameter(ValueFromPipelineByPropertyName = $true, Mandatory = $true)] [Alias('group_id')] [string]$GroupId ) process { $DuoRequest = @{ method = 'DELETE' path = '/admin/v1/administrative_units/{0}/group/{1}' -f $AdminUnitId, $GroupId } $Target = 'Unit {0} - Group {1}' -f $AdminUnitId, $GroupId if ($PSCmdlet.ShouldProcess($Target)) { Invoke-DuoRequest @DuoRequest } } } #EndRegion './Public/Admin API/Administrative Units/Remove-DuoGroupFromAdminUnit.ps1' 44 #Region './Public/Admin API/Administrative Units/Remove-DuoIntegrationFromAdminUnit.ps1' 0 function Remove-DuoGroupFromAdminUnit { <# .SYNOPSIS Remove Integration from Administrative Unit .DESCRIPTION Unassign the integration with admin_id from the administrative unit with admin_unit_id. Requires "Grant administrators" API permission. .PARAMETER AdminUnitId The ID of the Administrative Unit .PARAMETER IntegrationKey The Key of the Integration .EXAMPLE Remove-DuoIntegrationFromAdminUnit -AdminUnitId SOMEADMINUNITID -IntegrationKey SOMEINTEGRATIONKEY .LINK https://duo.com/docs/adminapi#remove-integration-from-administrative-unit #> [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')] Param( [Parameter(ValueFromPipelineByPropertyName = $true, Mandatory = $true)] [Alias('admin_unit_id')] [string]$AdminUnitId, [Parameter(ValueFromPipelineByPropertyName = $true, Mandatory = $true)] [Alias('integration_Key')] [string]$IntegrationKey ) process { $DuoRequest = @{ method = 'DELETE' path = '/admin/v1/administrative_units/{0}/integration/{1}' -f $AdminUnitId, $IntegrationKey } $Target = 'Unit {0} - Integration {1}' -f $AdminUnitId, $IntegrationKey if ($PSCmdlet.ShouldProcess($Target)) { Invoke-DuoRequest @DuoRequest } } } #EndRegion './Public/Admin API/Administrative Units/Remove-DuoIntegrationFromAdminUnit.ps1' 44 #Region './Public/Admin API/Administrative Units/Update-DuoAdminUnit.ps1' 0 function Update-DuoAdminUnit { <# .SYNOPSIS Modify Administrative Unit .DESCRIPTION Change the name, description, assigned administrators, groups, and/or integrations of the administrative unit with admin_unit_id. Requires "Grant administrators" API permission. .PARAMETER AdminUnitId The ID of the Administrative Unit .PARAMETER Name The new name of the administrative unit. Must be unique amongst all administrative units. .PARAMETER Description An updated description of the administrative unit. .PARAMETER RestrictByGroups Change whether the administrative unit specifies groups. Default: false. .PARAMETER RestrictByIntegrations Change whether the administrative unit specifies integrations. Default: false. .PARAMETER Admins One or more admin_id values to assign administrators to the new administrative unit. The administrator user must have restricted_by_admin_units set to true before attempting to assign them to an administrative unit via the API. .PARAMETER Groups One or more group_id values to assign groups to the new administrative unit. .PARAMETER Integrations One or more integration_key values to assign integrations to the new administrative unit. .EXAMPLE Update-DuoAdminUnit -AdminUnitId SOMEADMINUNITID -Name 'Accounts Payable Admins' -RestrictByGroups -Groups 'ACCTSPAYABLEGROUPID' .LINK https://duo.com/docs/adminapi#modify-administrative-unit #> [CmdletBinding(SupportsShouldProcess)] Param( [Parameter(Mandatory = $true)] [string]$AdminUnitId, [Parameter()] [string]$Name, [Parameter()] [string]$Description, [Parameter()] [switch]$RestrictByGroups, [Parameter()] [switch]$RestrictByIntegrations, [Parameter()] [string[]]$Admins, [Parameter()] [string[]]$Groups, [Parameter()] [string[]]$Integrations ) $Params = @{} if ($Name) { $Params.name = $Name } if ($Description) { $Params.description = $Description } if ($RestrictByGroups.IsPresent) { $Params.restrict_by_groups = $RestrictByGroups.IsPresent if ($Groups) { $Params.groups = $Groups } } if ($Admins) { $Params.admins = $Admins } if ($RestrictByIntegrations.IsPresent) { $Params.restrict_by_integrations = $RestrictByIntegrations.IsPresent if ($Integrations) { $Params.integrations = $Integrations } } $DuoRequest = @{ Method = 'POST' Path = '/admin/v1/admins/{0}' -f $AdminUnitId Params = $Params } if ($PSCmdlet.ShouldProcess($AdminUnitId)) { $Request = Invoke-DuoRequest @DuoRequest if ($Request.stat -ne 'OK') { $Request } else { $Request.response } } } #EndRegion './Public/Admin API/Administrative Units/Update-DuoAdminUnit.ps1' 97 #Region './Public/Admin API/Administrators/Clear-DuoAdminInactivity.ps1' 0 function Clear-DuoAdminInactivity { <# .SYNOPSIS Clear Administrator Expiration .DESCRIPTION Clear expiration for the administrator with admin_id after the admin has been expired due to inactivity. The administrator's status changes from "Expired" to the status applied to that admin before inactive expiration, and restores access to the Duo Admin Panel if the effective status is "Active". Requires "Grant administrators" API permission. .PARAMETER AdminId The ID of the Administrator .EXAMPLE Clear-DuoAdminInactivity -AdminId SOMEADMINID .LINK https://duo.com/docs/adminapi#clear-administrator-expiration .NOTES #> [CmdletBinding(SupportsShouldProcess)] Param( [Parameter(Mandatory = $true)] [string]$AdminId ) $DuoRequest = @{ Method = 'POST' Path = '/admin/v1/admins/{0}/clear_inactivity' -f $AdminId } if ($PSCmdlet.ShouldProcess($AdminId)) { $Request = Invoke-DuoRequest @DuoRequest if ($Request.stat -ne 'OK') { $Request } else { $Request.response } } } #EndRegion './Public/Admin API/Administrators/Clear-DuoAdminInactivity.ps1' 41 #Region './Public/Admin API/Administrators/Get-DuoAdminActivations.ps1' 0 function Get-DuoAdminActivations { <# .SYNOPSIS Retrieve Pending Administrator Activations .DESCRIPTION Returns a paged list of pending administrator activations. Requires "Grant administrators" API permission. .EXAMPLE Get-DuoAdminActivations .LINK https://duo.com/docs/adminapi#retrieve-pending-administrator-activations #> [CmdletBinding()] Param() process { $Path = '/admin/v1/admins/activations' $DuoRequest = @{ Method = 'GET' Path = $Path } Invoke-DuoPaginatedRequest -DuoRequest $DuoRequest } } #EndRegion './Public/Admin API/Administrators/Get-DuoAdminActivations.ps1' 31 #Region './Public/Admin API/Administrators/Get-DuoAdminAuthFactors.ps1' 0 function Get-DuoAdminAuthFactors { <# .SYNOPSIS Retrieve Administrator Authentication Factors .DESCRIPTION Retrieve a list of the secondary authentication methods permitted for administrator log on to the Duo Admin Panel. Requires "Grant administrators" API permission. .EXAMPLE Get-DuoAdminAuthFactors .LINK https://duo.com/docs/adminapi#retrieve-administrator-authentication-factors #> [CmdletBinding()] Param() process { $Path = '/admin/v1/allowed_auth_methods' $DuoRequest = @{ Method = 'GET' Path = $Path } $Request = Invoke-DuoRequest @DuoRequest if ($Request.stat -ne 'OK') { $Request } else { $Request.response } } } #EndRegion './Public/Admin API/Administrators/Get-DuoAdminAuthFactors.ps1' 35 #Region './Public/Admin API/Administrators/Get-DuoAdminPasswordManagement.ps1' 0 function Get-DuoAdminPasswordManagement { <# .SYNOPSIS Retrieve Admin External Password Management Status .DESCRIPTION Returns a paged list of administrators indicating whether they have been configured for external password management. Requires "Grant administrators" API permission. .EXAMPLE Get-DuoAdminPasswordManagement .LINK https://duo.com/docs/adminapi#retrieve-admin-external-password-management-status #> [CmdletBinding()] Param( [Parameter(ValueFromPipelineByPropertyName = $true)] [Alias('admin_id')] [string]$AdminId ) process { if ($AdminId) { $Path = '/admin/v1/admins/{0}/password_mgmt' -f $AdminId } else { $Path = '/admin/v1/password_mgmt' } $DuoRequest = @{ Method = 'GET' Path = $Path } if ($AdminId) { $Request = Invoke-DuoRequest @DuoRequest if ($Request.stat -ne 'OK') { $Request } else { $Request.response } } else { Invoke-DuoPaginatedRequest -DuoRequest $DuoRequest } } } #EndRegion './Public/Admin API/Administrators/Get-DuoAdminPasswordManagement.ps1' 47 #Region './Public/Admin API/Administrators/Get-DuoAdmins.ps1' 0 function Get-DuoAdmins { <# .SYNOPSIS Retrieve Administrators .DESCRIPTION Returns a single admin or a paged list of administrators. Requires "Grant administrators" API permission. .PARAMETER AdminId The ID of the Adminstrator .EXAMPLE Get-DuoAdmins .LINK https://duo.com/docs/adminapi#retrieve-administrators #> [CmdletBinding()] Param( [Parameter(ValueFromPipelineByPropertyName = $true)] [Alias('admin_id')] [string]$AdminId ) process { if ($AdminId) { $Path = '/admin/v1/admins/{0}' -f $AdminId } else { $Path = '/admin/v1/admins' } $DuoRequest = @{ Method = 'GET' Path = $Path } if ($AdminId) { $Request = Invoke-DuoRequest @DuoRequest if ($Request.stat -ne 'OK') { $Request } else { $Request.response } } else { Invoke-DuoPaginatedRequest -DuoRequest $DuoRequest } } } Set-Alias -Name Get-DuoAdmin -Value Get-DuoAdmins #EndRegion './Public/Admin API/Administrators/Get-DuoAdmins.ps1' 52 #Region './Public/Admin API/Administrators/New-DuoAdmin.ps1' 0 function New-DuoAdmin { <# .SYNOPSIS Create Administrator .DESCRIPTION Create a new administrator. Requires "Grant administrators" API permission. .PARAMETER Email Valid email address for the new administrator. .PARAMETER Name Name for the new administrator. .PARAMETER Phone Phone number for the new administrator; E.164 format recommended (i.e. "+17345551212"). If no leading plus sign is provided then it is assumed to be a United States number and an implicit "+1" country code is prepended. Dashes and spaces are ignored. If this parameter is specified it cannot be empty. .PARAMETER Role The administrator's role. One of: "Owner", "Administrator", "Application Manager", "User Manager", "Help Desk", "Billing", "Phishing Manager", or "Read-only". The role names are case-sensitive. Defaults to "Owner" if not specified. Roles other than "Owner" are effective only if the customer edition includes the Administrative Roles feature. .PARAMETER RestrictedByAdminUnits Is this administrator restricted by an administrative unit assignment? Either true or false. Defaults to false if not specified. Must be set to true in order to add the admin to an administrative unit using the API. Note that attempting to set to true for admins with the "Owner" role results in a failure response. .PARAMETER SendEmail If set to 1, the activation link and an introductory message will be emailed to the new administrator. If set to 0, no email is sent, and the link is returned to the API method's caller only. Default: 0. .PARAMETER TokenId The token_id of the hardware token to associate with the administrator. .PARAMETER ValidDays Number of days before the activation link expires. Default: 7 Maximum:: 31 .EXAMPLE New-DuoAdmin -Email peter.gibbons@initech.com -Name 'Peter Gibbons' .LINK https://duo.com/docs/adminapi#create-administrator #> [CmdletBinding(SupportsShouldProcess)] Param( [Parameter(Mandatory = $true)] [string]$Email, [Parameter(Mandatory = $true)] [string]$Name, [Parameter()] [string]$Phone, [Parameter()] [ValidateSet('Owner', 'Administrator', 'Application Manager', 'User Manager', 'Help Desk', 'Billing', 'Phishing Manager', 'Read-Only')] [string]$Role, [Parameter()] [switch]$RestrictedByAdminUnits, [Parameter()] [switch]$SendEmail, [Parameter()] [string]$TokenId, [Parameter()] [ValidateRange(1, 31)] [int]$ValidDays ) $Params = @{ email = $Email name = $Name } if ($Phone) { $Params.phone = $Phone } if ($Role) { $Params.role = $Role } if ($RestrictedByAdminUnits.IsPresent) { $Params.restricted_by_admin_units = $RestrictedByAdminUnits.IsPresent } if ($SendEmail.IsPresent) { $Params.send_email = 1 } if ($TokenId) { $Params.token_id = $TokenId } if ($ValidDays) { $Params.valid_days = $ValidDays } $DuoRequest = @{ Method = 'POST' Path = '/admin/v1/admins' Params = $Params } if ($PSCmdlet.ShouldProcess($Email)) { $Request = Invoke-DuoRequest @DuoRequest if ($Request.stat -ne 'OK') { $Request } else { $Request.response } } } #EndRegion './Public/Admin API/Administrators/New-DuoAdmin.ps1' 100 #Region './Public/Admin API/Administrators/New-DuoAdminActivation.ps1' 0 function New-DuoAdminActivation { <# .SYNOPSIS Create Administrator Activation Link .DESCRIPTION Create a link to the activation form for a new administrator with email address email. The administrator will not actually be created until the activation form is completed with further information (like the administrator's name and phone number). Requires "Grant administrators" API permission. .PARAMETER Email Email address for the new administrator. Must not already be in use by any other administrator or pending administrator activation. .PARAMETER AdminName The full name of the administrator. The administrator's email will be used as the name if not specified. .PARAMETER AdminRole The administrator's role. One of: "Owner", "Administrator", "Application Manager", "User Manager", "Help Desk", "Billing", "Phishing Manager", or "Read-only". The role names are case-sensitive. Defaults to "Owner" if not specified. Roles other than "Owner" are effective only if the customer edition includes the Administrative Roles feature. .PARAMETER SendEmail If set to 1, the activation link and an introductory message will be emailed to the new administrator. If set to 0, no email is sent, and the link is returned to the API method's caller only. Default: 0. .PARAMETER ValidDays Number of days before the link expires. Default: 7 Maximum: 31 .EXAMPLE New-DuoAdminActivation -Email peter.gibbons@initech.com .LINK https://duo.com/docs/adminapi#create-administrator-activation-link #> [CmdletBinding(SupportsShouldProcess)] Param( [Parameter(Mandatory = $true)] [string]$Email, [Parameter()] [string]$AdminName, [Parameter()] [ValidateSet('Owner', 'Administrator', 'Application Manager', 'User Manager', 'Help Desk', 'Billing', 'Phishing Manager', 'Read-Only')] [string]$AdminRole, [Parameter()] [Switch]$SendEmail, [Parameter()] [ValidateRange(1, 31)] [int]$ValidDays ) $Params = @{ email = $Email } if ($AdminName) { $Params.admin_name = $AdminName } if ($AdminRole) { $Params.admin_role = $AdminRole } if ($SendEmail.IsPresent) { $Params.send_email = 1 } if ($ValidDays) { $Params.valid_days = $ValidDays } $DuoRequest = @{ Method = 'POST' Path = '/admin/v1/admins/activations' Params = $Params } if ($PSCmdlet.ShouldProcess($Email)) { $Request = Invoke-DuoRequest @DuoRequest if ($Request.stat -ne 'OK') { $Request } else { $Request.response } } } #EndRegion './Public/Admin API/Administrators/New-DuoAdminActivation.ps1' 75 #Region './Public/Admin API/Administrators/New-DuoAdminActivationLink.ps1' 0 function New-DuoAdminActivationLink { <# .SYNOPSIS Create Activation Link for Administrator Pending Activation .DESCRIPTION Creates an activation link for the administrator pending activation with the administrator ID admin_id. There must not already be an existing activation link for the admin. Requires "Grant administrators" API permission. .PARAMETER AdminId The ID of the Administrator .EXAMPLE New-DuoAdminActivationLink -AdminId SOMEADMINID .LINK https://duo.com/docs/adminapi#create-activation-link-for-administrator-pending-activation #> [CmdletBinding(SupportsShouldProcess)] Param( [Parameter(Mandatory = $true)] [string]$AdminId ) $DuoRequest = @{ Method = 'POST' Path = '/admin/v1/admins/{0}/activation_link' -f $AdminId Params = $Params } if ($PSCmdlet.ShouldProcess($AdminId)) { $Request = Invoke-DuoRequest @DuoRequest if ($Request.stat -ne 'OK') { $Request } else { $Request.response } } } #EndRegion './Public/Admin API/Administrators/New-DuoAdminActivationLink.ps1' 40 #Region './Public/Admin API/Administrators/Remove-DuoAdmin.ps1' 0 function Remove-DuoAdmin { <# .SYNOPSIS Delete Administrator .DESCRIPTION Delete the administrator with administrator ID admin_id from the system. Administrators managed by directory sync can not be deleted via API. Requires "Grant administrators" API permission. .PARAMETER AdminId The ID of the Administrator .EXAMPLE Remove-DuoAdmin -AdminId SOMEADMINID .LINK https://duo.com/docs/adminapi#delete-administrator #> [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')] Param( [Parameter(ValueFromPipelineByPropertyName = $true, Mandatory = $true)] [Alias('admin_id')] [string]$AdminId ) process { $DuoRequest = @{ method = 'DELETE' path = '/admin/v1/admins/{0}' -f $AdminId } if ($PSCmdlet.ShouldProcess($AdminId)) { Invoke-DuoRequest @DuoRequest } } } #EndRegion './Public/Admin API/Administrators/Remove-DuoAdmin.ps1' 34 #Region './Public/Admin API/Administrators/Remove-DuoAdminActivation.ps1' 0 function Remove-DuoAdminActivation { <# .SYNOPSIS Delete Pending Administrator Activation .DESCRIPTION Delete the pending admin activation with ID admin_activation_id from the system. Requires "Grant administrators" API permission. .PARAMETER AdminActivationId The ID of the Administrator activation .EXAMPLE Remove-DuoAdminActivation -AdminActivationId SOMEACTIVATIONID .LINK https://duo.com/docs/adminapi#delete-pending-administrator-activation #> [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')] Param( [Parameter(ValueFromPipelineByPropertyName = $true, Mandatory = $true)] [Alias('admin_activation_id')] [string]$AdminActivationId ) process { $DuoRequest = @{ method = 'DELETE' path = '/admin/v1/admins/activations/{0}' -f $AdminActivationId } if ($PSCmdlet.ShouldProcess($AdminId)) { Invoke-DuoRequest @DuoRequest } } } #EndRegion './Public/Admin API/Administrators/Remove-DuoAdminActivation.ps1' 35 #Region './Public/Admin API/Administrators/Remove-DuoAdminActivationLink.ps1' 0 function Remove-DuoAdminActivationLink { <# .SYNOPSIS Delete Activation Link from Administrator Pending Activation .DESCRIPTION Deletes and invalidates the current activation link from the administrator pending activation with the administrator ID admin_id. Requires "Grant administrators" API permission. .PARAMETER AdminId The ID of the Administrator .EXAMPLE Remove-DuoAdminActivationLink -AdminId SOMEADMINID .LINK https://duo.com/docs/adminapi#delete-activation-link-from-administrator-pending-activation #> [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')] Param( [Parameter(ValueFromPipelineByPropertyName = $true, Mandatory = $true)] [Alias('admin_id')] [string]$AdminId ) process { $DuoRequest = @{ method = 'DELETE' path = '/admin/v1/admins/{0}/activation_link' -f $AdminId } if ($PSCmdlet.ShouldProcess($AdminId)) { Invoke-DuoRequest @DuoRequest } } } #EndRegion './Public/Admin API/Administrators/Remove-DuoAdminActivationLink.ps1' 35 #Region './Public/Admin API/Administrators/Reset-DuoAdminAuthAttempts.ps1' 0 function Reset-DuoAdminAuthAttempts { <# .SYNOPSIS Reset Administrator Authentication Attempts .DESCRIPTION Clear the number of failed login attempts for the administrator with admin_id. Re-enables an administrator who has been disabled due to too many failed authentication attempts. Requires "Grant administrators" API permission. .PARAMETER AdminId The ID of the Administrator .EXAMPLE Reset-DuoAdminAuthAttempts -AdminId SOMEADMINID .LINK https://duo.com/docs/adminapi#reset-administrator-authentication-attempts #> [CmdletBinding(SupportsShouldProcess)] Param( [Parameter(Mandatory = $true)] [string]$AdminId ) $DuoRequest = @{ Method = 'POST' Path = '/admin/v1/admins/{0}/reset' -f $AdminId } if ($PSCmdlet.ShouldProcess($AdminId)) { $Request = Invoke-DuoRequest @DuoRequest if ($Request.stat -ne 'OK') { $Request } else { $Request.response } } } #EndRegion './Public/Admin API/Administrators/Reset-DuoAdminAuthAttempts.ps1' 39 #Region './Public/Admin API/Administrators/Send-DuoAdminActivationEmail.ps1' 0 function Send-DuoAdminActivationEmail { <# .SYNOPSIS Email Activation Link to Administrator Pending Activation .DESCRIPTION Email the current activation link to the administrator pending activation with the administrator ID admin_id. Requires "Grant administrators" API permission. .PARAMETER AdminId The ID of the Administrator .EXAMPLE Send-DuoAdminActivationEmail -AdminId SOMEADMINID .LINK https://duo.com/docs/adminapi#email-activation-link-to-administrator-pending-activation #> [CmdletBinding(SupportsShouldProcess)] Param( [Parameter(Mandatory = $true)] [string]$AdminId ) $DuoRequest = @{ Method = 'POST' Path = '/admin/v1/admins/{0}/activation_link/email' -f $AdminId } if ($PSCmdlet.ShouldProcess($AdminId)) { $Request = Invoke-DuoRequest @DuoRequest if ($Request.stat -ne 'OK') { $Request } else { $Request.response } } } #EndRegion './Public/Admin API/Administrators/Send-DuoAdminActivationEmail.ps1' 39 #Region './Public/Admin API/Administrators/Set-DuoAdminAuthFactors.ps1' 0 function Set-DuoAdminAuthFactors { <# .SYNOPSIS Restrict Administrator Authentication Factors .DESCRIPTION Enable or disable secondary authentication methods permitted for administrator log on to the Duo Admin Panel. When no methods are restricted Duo administrators may use any available two-factor method. Any method not explicitly set to true in the POST is disabled. Requires "Grant administrators" API permission. .PARAMETER HardwareTokenEnabled If true, administrators may authenticate to the Duo Admin Panel with an OTP hardware token. If false or not specified, administrators may not use OTP hardware tokens. .PARAMETER MobileOtpEnabled If true, administrators may authenticate to the Duo Admin Panel with a passcode generated by the Duo Mobile app. If false or not specified, administrators may not use Duo Mobile passcodes. .PARAMETER PushEnabled If true, administrators may authenticate to the Duo Admin Panel by approving a push request in the Duo Mobile app. If false or not specified, administrators may not approve login with Duo Push. .PARAMETER SmsEnabled If true, administrators may authenticate to the Duo Admin Panel with a passcode received via SMS. If false or not specified, administrators may not use SMS passcodes. .PARAMETER VoiceEnabled If true, administrators may authenticate to the Duo Admin Panel by approving the request received via phone call. If false or not specified, administrators may not approve login with a phone call. .PARAMETER YubikeyEnabled If true, administrators may authenticate to the Duo Admin Panel with a Yubikey token. If false or not specified, administrators may not use Yubikey tokens. .EXAMPLE Set-DuoAdminAuthFactors -HardwareTokenEnabled -MobileOtpEnabled -PushEnabled -YubiKeyEnabled .LINK https://duo.com/docs/adminapi#restrict-administrator-authentication-factors #> [CmdletBinding(SupportsShouldProcess)] Param( [Parameter()] [switch]$HardwareTokenEnabled, [Parameter()] [switch]$MobileOtpEnabled, [Parameter()] [switch]$PushEnabled, [Parameter()] [switch]$SmsEnabled, [Parameter()] [switch]$VoiceEnabled, [Parameter()] [switch]$YubikeyEnabled ) process { $Params = @{} if ($HardwareTokenEnabled.IsPresent) { $Params.hardware_token_enabled = $HardwareTokenEnabled.IsPresent } if ($MobileOtpEnabled.IsPresent) { $Params.mobile_otp_enabled = $MobileOtpEnabled.IsPresent } if ($PushEnabled.IsPresent) { $Params.push_enabled = $PushEnabled.IsPresent } if ($SmsEnabled.IsPresent) { $Params.sms_enabled = $SmsEnabled.IsPresent } if ($VoiceEnabled.IsPresent) { $Params.voice_enabled = $VoiceEnabled.IsPresent } if ($YubikeyEnabled.IsPresent) { $Params.yubikey_enabled = $YubikeyEnabled.IsPresent } $DuoRequest = @{ Method = 'POST' Path = '/admin/v1/allowed_auth_methods' Params = $Params } if ($PSCmdlet.ShouldProcess('AuthFactors')) { $Request = Invoke-DuoRequest @DuoRequest if ($Request.stat -ne 'OK') { $Request } else { $Request.response } } } } #EndRegion './Public/Admin API/Administrators/Set-DuoAdminAuthFactors.ps1' 81 #Region './Public/Admin API/Administrators/Sync-DuoAdminFromDirectory.ps1' 0 function Sync-DuoAdminFromDirectory { <# .SYNOPSIS Synchronize Admin from Directory .DESCRIPTION Initiate a sync to create, update, or mark for deletion the administrator specified by email against the directory specified by the directory_key. The directory_key for a directory can be found by navigating to Administrators -> Admin Directory Sync in the Duo Admin Panel, and then clicking on the configured directory. Learn more about syncing individual admins from Active Directory, OpenLDAP, or Azure Active Directory. Requires "Grant administrators" API permission. .PARAMETER DirectoryKey The directory key to sync from .PARAMETER Email Email address of the admin to update or create via directory sync. This should be the same as the value for the admin's email attribute in the source directory as configured in the sync. Administrators with "Owner" role may not be synced. .EXAMPLE Sync-DuoAdminFromDirectory -DirectoryKey 123456 -Email admin@domain.com .LINK https://duo.com/docs/adminapi#synchronize-admin-from-directory #> [CmdletBinding(SupportsShouldProcess)] Param( [Parameter(Mandatory = $true)] [Alias('directory_key')] [string]$DirectoryKey, [Parameter(Mandatory = $true)] [string]$Email ) Process { $Params = @{ email = $Email } $DuoRequest = @{ Method = 'POST' Path = '/admin/v1/admins/directorysync/{0}/syncadmin' -f $DirectoryKey Params = $Params } if ($PSCmdlet.ShouldProcess($Email)) { $Request = Invoke-DuoRequest @DuoRequest if ($Request.stat -ne 'OK') { $Request } else { $Request.response } } } } #EndRegion './Public/Admin API/Administrators/Sync-DuoAdminFromDirectory.ps1' 53 #Region './Public/Admin API/Administrators/Update-DuoAdmin.ps1' 0 function Update-DuoAdmin { <# .SYNOPSIS Modify Administrator .DESCRIPTION Change the name, phone number, or other properties of the administrator with the administrator ID admin_id. Requires "Grant administrators" API permission. .PARAMETER AdminId The ID of the administrator .PARAMETER Name New name for the administrator. Read-only if the admin is managed by directory sync. .PARAMETER Phone The phone number; E.164 format recommended (i.e. "+17345551212"). If no leading plus sign is provided then it is assumed to be a United States number and an implicit "+1" country code is prepended. Dashes and spaces are ignored. If this parameter is specified it cannot be empty. .PARAMETER PasswordChangeRequested Specify true to require that the admin pick a new password at their next login, or false if no password change is required. May not be changed to true if the admin has external password management enabled. .PARAMETER Role New role for the administrator. One of: "Owner", "Administrator", "Application Manager", "User Manager", "Help Desk", "Billing", "Phishing Manager", or "Read-only". The role names are case-sensitive. Roles other than "Owner" are effective only if the customer edition includes the Administrative Roles feature. Read-only if the admin is managed by directory sync. .PARAMETER RestrictedByAdminUnits Is this administrator restricted by an administrative unit assignment? Either true or false. Must be set to true in order to add the admin to an administrative unit using the API. Note that attempting to set to true for admins with the "Owner" role results in a failure response. .PARAMETER Status The desired administrator account status. Either "Active" or "Disabled" (case-sensitive). Administrators with the "Owner" role may not be disabled via API. To clear an inactive admin's "Expired" status, see Clear Administrator Expiration. Read-only if the admin is managed by directory sync. .PARAMETER TokenId The ID of the hardware token to associate with the administrator. Specify with no value to remove any existing token assignment for that administrator. .EXAMPLE Update-DuoAdmin -AdminId SOMEADMINID -Status Disabled .LINK https://duo.com/docs/adminapi#modify-administrator #> [CmdletBinding(SupportsShouldProcess)] Param( [Parameter(ValueFromPipelineByPropertyName = $true, Mandatory = $true)] [Alias('admin_id')] [string]$AdminId, [Parameter()] [string]$Name, [Parameter()] [string]$Phone, [Parameter()] [switch]$PasswordChangeRequested, [Parameter()] [ValidateSet('Owner', 'Administrator', 'Application Manager', 'User Manager', 'Help Desk', 'Billing', 'Phishing Manager', 'Read-Only')] [string]$Role, [Parameter()] [switch]$RestrictedByAdminUnits, [Parameter()] [ValidateSet('Active', 'Disabled')] [string]$Status, [Parameter()] [string]$TokenId ) process { $Params = @{} if ($Name) { $Params.name = $Name } if ($Phone) { $Params.phone = $Phone } if ($PasswordChangeRequested.IsPresent) { $Params.password_change_requested = $PasswordChangeRequested.IsPresent } if ($Role) { $Params.role = $Role } if ($RestrictedByAdminUnits.IsPresent) { $Params.restricted_by_admin_units = $RestrictedByAdminUnits.IsPresent } if ($Status) { $Params.status = $Status } if ($TokenId) { $Params.token_id = $TokenId } $DuoRequest = @{ Method = 'POST' Path = '/admin/v1/admins/{0}' -f $AdminId Params = $Params } if ($PSCmdlet.ShouldProcess($AdminId)) { $Request = Invoke-DuoRequest @DuoRequest if ($Request.stat -ne 'OK') { $Request } else { $Request.response } } } } #EndRegion './Public/Admin API/Administrators/Update-DuoAdmin.ps1' 99 #Region './Public/Admin API/Logs/Get-DuoAdminLog.ps1' 0 function Get-DuoAdminLog { <# .SYNOPSIS Administrator Logs .DESCRIPTION Returns a list of administrator log events. Only the 1000 earliest events will be returned; you may need to call this multiple times with mintime to page through the entire log. Requires "Grant read log" API permission. .PARAMETER MinTime Limit report to events after this Unix timestamp. .EXAMPLE Get-DuoAdminLog .LINK https://duo.com/docs/adminapi#administrator-logs .NOTES We recommend requesting logs no more than once per minute. #> [CmdletBinding(DefaultParameterSetName = 'None')] Param( [Parameter(ParameterSetName = 'UnixTime')] [string]$MinTime, [Parameter(ParameterSetName = 'DateTime')] [string]$StartDate, [Parameter(ParameterSetName = 'Days')] [int]$Days, [Parameter(ParameterSetName = 'Hours')] [int]$Hours ) if ($StartDate) { $MinTime = $StartDate | Get-Date -UFormat '%s' } if ($Days) { $MaxTime = [int64](Get-Date -UFormat '%s') $MinTime = $MaxTime - [int64](86400 * $Days) } if ($Hours) { $MaxTime = [int64](Get-Date -UFormat '%s') $MinTime = $MaxTime - [int64](3600 * $Hours) } $Params = @{} if ($MinTime) { $Params.mintime = $MinTime.ToString() } $DuoRequest = @{ Method = 'GET' Path = '/admin/v1/logs/administrator' Params = $Params } $Request = Invoke-DuoRequest @DuoRequest if ($Request.stat -ne 'OK') { $Request } else { $Request.response } } Set-Alias -Name Get-DuoAdminLogs -Value Get-DuoAdminLog #EndRegion './Public/Admin API/Logs/Get-DuoAdminLog.ps1' 72 #Region './Public/Admin API/Logs/Get-DuoAuthLog.ps1' 0 function Get-DuoAuthLog { <# .SYNOPSIS Authentication Logs .DESCRIPTION Returns a paged list of authentication log events ranging from the last 180 days up to as recently as two minutes before the API request. Requires "Grant read log" API permission. .PARAMETER Days Number of days to retrieve with max time of now .PARAMETER Hours Number of hours to retrieve with max time of now .PARAMETER StartDate The start date for log entries .PARAMETER EndDate The end date for log enties .PARAMETER MinTime Return records that have a 13 character Unix timestamp in milliseconds of mintime or later. This value must be strictly less then maxtime. .PARAMETER MaxTime Return records that have a 13 character Unix timestamp in milliseconds of maxtime or earlier. This value must be strictly greater then mintime. .PARAMETER Applications An integration's integration_key or the key value for an application returned in the authentication log output. Default: Return logs for all applications. .PARAMETER Users A user's user_id or the key value for a user returned in the authentication log output. Default: Return logs for all users. .PARAMETER EventTypes The type of authentication event. One of: | Value | Description | |-------|-------------| | authentication | Return events for authentication attempts. | enrollment | Return events related to a user completing Duo's inline enrollment. .PARAMETER Factors The factor or method used for an authentication attempt. One of: | Value | Description | |-------|-------------| | duo_push | Return events where the authentication factor was "Duo Push". | phone_call | Return events where the authentication factor was a phone call. | u2f_token | Return events where the authentication factor was a U2F token. | hardware_token | Return events where the authentication factor was a hardware token passcode. | bypass_code | Return events where the authentication factor was a bypass code. | sms_passcode | Return events where the authentication factor was an SMS passcode. | duo_mobile_passcode | Return events where the authentication factor was a passcode generated by "Duo Mobile". | yubikey_code | Return events where the authentication factor was a Yubikey OTP token passcode. | passcode | Return events where the authentication factor was a passcode not identified as another known type. | digipass_go_7_token | Return events where the authentication factor was a Digipass GO 7 token purchased from Duo. | WebAuthn Security Key | Return events where the authentication factor was a FIDO2 security key. | WebAuthn Chrome Touch ID | Return events where the authentication factor was Apple Touch ID with the Chrome browser. | WebAuthn Credential | Return events where the authentication factor was a WebAuthn authenticator other than a security key or Touch ID. | not_available | Return events where the authentication factor is not available. | sms_refresh | Return events where the user requested a refresh batch of SMS passcodes. | remembered_device | Return events where the authentication factor was the remembered device token from a previous authentication success. | trusted_network | Return events where the effective authentication factor was an authorized network. | trusted_mobile_authenticator | Return events where the effective authentication factor Duo Mobile Inline Auth on an Android or iOS device. .PARAMETER Groups A group's group_id or the key value for a group returned in the authentication log output. Default: Return logs for all groups. .PARAMETER PhoneNumbers A phone's number as returned in the authentication log output. If the phone has been given a text name then both are returned in the format name (number). Default: Return logs for all phone numbers used. .PARAMETER Reasons The reason associated with an authentication attempt. One of: | Value | Description | |-------|-------------| | user_marked_fraud | Return events where authentication was denied because the end user explicitly marked "fraudulent". | deny_unenrolled_user | Return events where authentication was denied because of the following policy: "deny not enrolled users". | error | Return events where authentication was denied because of an error. | locked_out | Return events generated by users that are locked out. |user_disabled | Return events where authentication was denied because the user was disabled. | user_cancelled | Return events where authentication was denied because the end user cancelled the request. | invalid_passcode | Return events where authentication was denied because the passcode was invalid. | no_response | Return events where authentication was denied because there was no response from the user. | no_keys_pressed | Return events where authentication was denied because no keys were pressed to accept the auth. | call_timed_out | Return events where authentication was denied because the call was not answered or call authentication timed out for an indeterminate reason. | location_restricted | Return events where authentication was denied because the end user's location was restricted. | factor_restricted | Return events where authentication was denied because the authentication method used was not allowed. | platform_restricted | Return events where authentication was denied because the access platform was not allowed. | version_restricted | Return events where authentication was denied because the software version was not allowed. | rooted_device | Return events where authentication was denied because the approval device was rooted. | no_screen_lock | Return events where authentication was denied because the approval device does not have screen lock enabled. | touch_id_disabled | Return events where authentication was denied because the approval device's biometrics (fingerprint, Face ID or Touch ID) is disabled. | no_disk_encryption | Return events where authentication was denied because the approval device did not have disk encryption enabled. | anonymous_ip | Return events where authentication was denied because the authentication request came from an anonymous IP address. | out_of_date | Return events where authentication was denied because the software was out of date. | denied_by_policy | Return events where authentication was denied because of a policy. | software_restricted | Return events where authentication was denied because of software restriction. | no_duo_certificate_present | Return events where authentication was denied because there was no Duo certificate present. | user_provided_invalid_certificate | Return events where authentication was denied because an invalid management certificate was provided. | could_not_determine_if_endpoint_was_trusted | Return events where authentication was denied because it could not be determined if the endpoint was trusted. | invalid_management_certificate_collection_state | Return events where authentication was denied because of an invalid management certificate collection state. | no_referring_hostname_provided | Return events where authentication was denied because no referring hostname was provided. | invalid_referring_hostname_provided | Return events where authentication was denied because an invalid referring hostname was provided. | no_web_referer_match | Return events where authentication was denied because an invalid referring hostname did not match an application's hostnames list. | endpoint_failed_google_verification | Return events where authentication was denied because the endpoint failed Google verification. | endpoint_is_not_trusted | Return events where authentication was denied because the endpoint was not trusted. | invalid_device | Return events where authentication was denied because the device was invalid. | anomalous_push | Return events where authentication was denied because of an anomalous push. | endpoint_is_not_in_management_system | Return events where authentication was denied because the endpoint is not in a management system. | no_activated_duo_mobile_account | Return events where authentication was denied because the end user does not have an activated Duo Mobile app account. | allow_unenrolled_user | Return events where authentication was successful because of the following policy: "allow not enrolled users". | bypass_user | Return events where authentication was successful because a bypass code was used. | trusted_network | Return events where authentication was successful because the end user was on a trusted network. | remembered_device | Return events where authentication was successful because the end user was on a remembered device. | trusted_location | Return events where authentication was successful because the end user was in a trusted location. | user_approved | Return events where authentication was successful because the end user approved the authentication request. | valid_passcode | Return events where authentication was successful because the end user used a valid passcode. | allowed_by_policy | Return events where authentication was successful because of a policy. | allow_unenrolled_user_on_trusted_network | Return events where authentication was successful because the unenrolled user's access device was on an authorized network. | user_not_in_permitted_group | Return events where authentication was denied because the user did not belong to one of the Permitted Groups specified in the application's settings. Default: Return logs for any result. Filtering on all values is equivalent to the default. Note that enrollment events have no associated reason. .PARAMETER Results The result of an authentication attempt. One of: | Value | Description | |-------|-------------| | success | Return "successful" authentication events. | denied | Return "denied" authentication events. | fraud | Return "fraudulent" authentication events. Default: Return logs for any result. Filtering on all values is equivalent to the default. .PARAMETER Tokens A WebAuthn security key's webauthnkey or U2F security key's registration_id as returned in the authentication log output. Default: Return logs for security keys used. .LINK https://duo.com/docs/adminapi#authentication-logs .EXAMPLE Get-DuoAuthLog -Days 30 -EventTypes authentication -Factors duo_push -Results denied .NOTES There is an intentional two minute delay in availability of new authentications in the API response. Duo operates a large scale distributed system, and this two minute buffer period ensures that calls will return consistent results. Querying for results more recent than two minutes will return as empty. We recommend requesting logs no more than once per minute. The v2 handler provides new filtering and querying capabilities unavailable in the legacy v1 handler. This includes the ability to filter on users, groups, applications, authentication results, factors, and time ranges. #> [CmdletBinding()] Param( [Parameter(Mandatory = $true, ParameterSetName = 'Days')] [int]$Days, [Parameter(Mandatory = $true, ParameterSetName = 'Hours')] [int]$Hours, [Parameter(Mandatory = $true, ParameterSetName = 'MinMaxTime')] [string]$MinTime, [Parameter(Mandatory = $true, ParameterSetName = 'MinMaxTime')] [string]$MaxTime, [Parameter(Mandatory = $true, ParameterSetName = 'DateTime')] [datetime]$StartDate, [Parameter(ParameterSetName = 'DateTime')] [datetime]$EndDate, [Parameter()] [string[]]$Applications, [Parameter()] [string[]]$Users, [Parameter()] [ValidateSet('authentication', 'enrollment')] [string[]]$EventTypes, [Parameter()] [ValidateSet('duo_push', 'phone_call', 'u2f_token', 'hardware_token', 'bypass_code', 'sms_passcode', 'duo_mobile_passcode', 'yubikey_code', 'passcode', 'digipass_go_7_token', 'WebAuthn Security Key', 'not_available', 'sms_refresh', 'remembered_device', 'trusted_network', 'trusted_mobile_authenticator')] [string[]]$Factors, [Parameter()] [string[]]$Groups, [Parameter()] [string[]]$PhoneNumbers, [Parameter()] [ValidateSet('user_marked_fraud', 'deny_unenrolled_user', 'error', 'locked_out', 'user_disabled', 'user_cancelled', 'invalid_passcode', 'no_response', 'no_keys_pressed', 'call_timed_out', 'location_restricted', 'factor_restricted', 'platform_restricted', 'version_restricted', 'rooted_device', 'no_screen_lock', 'touch_id_disabled', 'no_disk_encryption', 'anonymous_ip', 'out_of_date', 'denied_by_policy', 'software_restricted', 'no_duo_certificate_present', 'user_provided_invalid_certificate', 'could_not_determine_if_endpoint_was_trusted', 'invalid_management_certificate_collection_state', 'no_referring_hostname_provided', 'invalid_referring_hostname_provided', 'no_web_referer_match', 'endpoint_failed_google_verification', 'endpoint_is_not_trusted', 'invalid_device', 'anomalous_push', 'endpoint_is_not_in_management_system', 'no_activated_duo_mobile_account', 'allow_unenrolled_user', 'bypass_user', 'trusted_network', 'remembered_device', 'trusted_location', 'user_approved', 'valid_passcode', 'allowed_by_policy', 'allow_unenrolled_user_on_trusted_network', 'user_not_in_permitted_group')] [string[]]$Reasons, [Parameter()] [ValidateSet('success', 'denied', 'fraud')] [string[]]$Results, [Parameter()] [string[]]$Tokens ) if ($Days) { $MaxTime = [int64](Get-Date -UFormat '%s000') $MinTime = $MaxTime - [int64](86400000 * $Days) } if ($Hours) { $MaxTime = [int64](Get-Date -UFormat '%s000') $MinTime = $MaxTime - [int64](3600000 * $Hours) } if ($StartDate) { if ($EndDate) { $MaxTime = $EndDate | Get-Date -UFormat '%s000' } else { $MaxTime = Get-Date -UFormat '%s000' } $MinTime = $StartDate | Get-Date -UFormat '%s000' } $Params = @{ mintime = $MinTime.ToString() maxtime = $MaxTime.ToString() } if ($Applications) { $Params.applications = @($Applications) } if ($Users) { $Params.users = @($Users) } if ($EventTypes) { $Params.event_types = @($EventTypes) } if ($Factors) { $Params.factors = @($Factors) } if ($Groups) { $Params.groups = @($Groups) } if ($PhoneNumbers) { $Params.phone_numbers = @($PhoneNumbers) } if ($Reasons) { $Params.reasons = @($Reasons) } if ($Results) { $Params.results = @($Results) } if ($Tokens) { $Params.tokens = @($Tokens) } $DuoRequest = @{ Method = 'GET' Path = '/admin/v2/logs/authentication' Params = $Params } $Response = Invoke-DuoRequest @DuoRequest if ($Response.stat -eq 'OK') { $Response.response.authlogs } else { $Response } } Set-Alias -Name Get-DuoAuthLogs -Value Get-DuoAuthLog #EndRegion './Public/Admin API/Logs/Get-DuoAuthLog.ps1' 263 #Region './Public/Admin API/Logs/Get-DuoOfflineEnrollmentLog.ps1' 0 function Get-DuoOfflineEnrollmentLog { <# .SYNOPSIS Offline Enrollment Logs .DESCRIPTION Returns a list of Duo Authentication for Windows Logon offline enrollment events ranging from the last 180 days up to as recently as two minutes before the API request. There is an intentional two minute delay in availability of new authentications in the API response. Duo operates a large scale distributed system, and this two minute buffer period ensures that calls will return consistent results. Querying for results more recent than two minutes will return as empty. Requires "Grant read log" API permission. The 1000 earliest events will be returned; you may need to call this multiple times with mintime to page through the entire log. Note that more or fewer than 1000 events may be returned depending on how many actual events exist for the specified mintime. .PARAMETER MinTime Only return records that have a Unix timestamp in seconds of mintime or later. Use mintime+1 to avoid receiving duplicate data. .EXAMPLE Get-DuoOfflineEnrollmentLog .LINK https://duo.com/docs/adminapi#offline-enrollment-logs .NOTES We recommend requesting logs no more than once per minute. #> [CmdletBinding(DefaultParameterSetName = 'None')] Param( [Parameter(ParameterSetName = 'UnixTime')] [string]$MinTime, [Parameter(ParameterSetName = 'DateTime')] [string]$StartDate, [Parameter(ParameterSetName = 'Days')] [int]$Days, [Parameter(ParameterSetName = 'Hours')] [int]$Hours ) if ($StartDate) { $MinTime = $StartDate | Get-Date -UFormat '%s' } if ($Days) { $MaxTime = [int64](Get-Date -UFormat '%s') $MinTime = $MaxTime - [int64](86400 * $Days) } if ($Hours) { $MaxTime = [int64](Get-Date -UFormat '%s') $MinTime = $MaxTime - [int64](3600 * $Hours) } $Params = @{} if ($MinTime) { $Params.mintime = $MinTime.ToString() } $DuoRequest = @{ Method = 'GET' Path = '/admin/v1/logs/offline_enrollment' Params = $Params } $Request = Invoke-DuoRequest @DuoRequest if ($Request.stat -ne 'OK') { $Request } else { $Request.response } } Set-Alias -Name Get-DuoOfflineEnrollmentLogs -Value Get-DuoOfflineEnrollmentLog #EndRegion './Public/Admin API/Logs/Get-DuoOfflineEnrollmentLog.ps1' 74 #Region './Public/Admin API/Logs/Get-DuoTelephonyLog.ps1' 0 function Get-DuoTelephonyLog { <# .SYNOPSIS Telephony Logs .DESCRIPTION Returns a list of telephony log events. Only the 1000 earliest events will be returned; you may need to call this multiple times with mintime to page through the entire log. Requires "Grant read log" API permission. .PARAMETER MinTime Limit report to events after this Unix timestamp. .EXAMPLE Get-DuoTelephonyLog .LINK https://duo.com/docs/adminapi#telephony-logs .NOTES We recommend requesting logs no more than once per minute. #> [CmdletBinding(DefaultParameterSetName = 'None')] Param( [Parameter(ParameterSetName = 'UnixTime')] [string]$MinTime, [Parameter(ParameterSetName = 'DateTime')] [string]$StartDate, [Parameter(ParameterSetName = 'Days')] [int]$Days, [Parameter(ParameterSetName = 'Hours')] [int]$Hours ) if ($StartDate) { $MinTime = $StartDate | Get-Date -UFormat '%s' } if ($Days) { $MaxTime = [int64](Get-Date -UFormat '%s') $MinTime = $MaxTime - [int64](86400 * $Days) } if ($Hours) { $MaxTime = [int64](Get-Date -UFormat '%s') $MinTime = $MaxTime - [int64](3600 * $Hours) } $Params = @{} if ($MinTime) { $Params.mintime = $MinTime.ToString() } $DuoRequest = @{ Method = 'GET' Path = '/admin/v1/logs/telephony' Params = $Params } $Request = Invoke-DuoRequest @DuoRequest if ($Request.stat -ne 'OK') { $Request } else { $Request.response } } Set-Alias -Name Get-DuoTelephonyLogs -Value Get-DuoTelephonyLog #EndRegion './Public/Admin API/Logs/Get-DuoTelephonyLog.ps1' 72 #Region './Public/Admin API/Groups/Get-DuoGroups.ps1' 0 function Get-DuoGroups { <# .SYNOPSIS Retrieve Groups .DESCRIPTION Returns a single group or a paged list of groups. Requires "Grant read resource" API permission. .PARAMETER GroupId Group Id to retrieve .EXAMPLE Get-DuoGroups .LINK https://duo.com/docs/adminapi#retrieve-groups .LINK https://duo.com/docs/adminapi#get-group-info #> [CmdletBinding()] Param( [Parameter(ValueFromPipelineByPropertyName = $true)] [Alias('group_id')] [string]$GroupId ) process { if ($GroupId) { $Path = '/admin/v2/groups/{0}' -f $GroupId } else { $Path = '/admin/v1/groups' $Params = @{ offset = 0 } } $DuoRequest = @{ Method = 'GET' Path = $Path } if ($Params) { $DuoRequest.Params = $Params Invoke-DuoPaginatedRequest -DuoRequest $DuoRequest } else { $Request = Invoke-DuoRequest @DuoRequest if ($Request.stat -ne 'OK') { $Request } else { $Request.response } } } } Set-Alias -Name Get-DuoGroup -Value Get-DuoGroups #EndRegion './Public/Admin API/Groups/Get-DuoGroups.ps1' 56 #Region './Public/Admin API/Groups/Get-DuoGroupUsers.ps1' 0 function Get-DuoGroupUsers { <# .SYNOPSIS Retrieve Group Members .DESCRIPTION Returns a paged list of members of a specified group. .PARAMETER GroupId Group Id to get member list for .EXAMPLE Get-DuoGroupUsers -GroupId SOMEDUOID .LINK https://duo.com/docs/adminapi#get-group-info #> [CmdletBinding()] Param( [Parameter(ValueFromPipelineByPropertyName = $true, Mandatory = $true)] [Alias('group_id')] [string]$GroupId ) process { $DuoRequest = @{ Method = 'GET' Path = '/admin/v2/groups/{0}/users' -f $GroupId Params = @{ offset = 0 } } Invoke-DuoPaginatedRequest -DuoRequest $DuoRequest } } #EndRegion './Public/Admin API/Groups/Get-DuoGroupUsers.ps1' 35 #Region './Public/Admin API/Groups/New-DuoGroup.ps1' 0 function New-DuoGroup { <# .SYNOPSIS Create Group .DESCRIPTION Create a new group. Requires "Grant write resource" API permission. .PARAMETER Name The name of the group. .PARAMETER Description The description of the group. .PARAMETER Status The authentication status of the group. .EXAMPLE New-DuoGroup -Name 'Testing Group' -Description 'This is for testing purposes' -Status 'Active' .LINK https://duo.com/docs/adminapi#create-group .NOTES #> [CmdletBinding(SupportsShouldProcess)] Param( [Parameter(Mandatory = $true)] [string]$Name, [Parameter()] [string]$Description = '', [Parameter()] [ValidateSet('Active', 'Bypass', 'Disabled')] [string]$Status = 'Active' ) process { $Params = @{ name = $Name desc = $Description status = $Status.ToLower() } $DuoRequest = @{ Method = 'POST' Path = '/admin/v1/groups' Params = $Params } if ($PSCmdlet.ShouldProcess($Name)) { $Request = Invoke-DuoRequest @DuoRequest if ($Request.stat -ne 'OK') { $Request } else { $Request.response } } } } #EndRegion './Public/Admin API/Groups/New-DuoGroup.ps1' 63 #Region './Public/Admin API/Groups/Remove-DuoGroup.ps1' 0 function Remove-DuoGroup { <# .SYNOPSIS Delete Group .DESCRIPTION Delete a group. Requires "Grant write resource" API permission. .PARAMETER GroupId Group Id to remove .EXAMPLE Remove-DuoGroup -GroupId SOMEDUOID .LINK https://duo.com/docs/adminapi#delete-group #> [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')] Param( [Parameter(ValueFromPipelineByPropertyName = $true, Mandatory = $true)] [Alias('group_id')] [string]$GroupId ) process { $DuoRequest = @{ method = 'DELETE' path = '/admin/v1/groups/{0}' -f $GroupId } if ($PSCmdlet.ShouldProcess($GroupId)) { Invoke-DuoRequest @DuoRequest } } } #EndRegion './Public/Admin API/Groups/Remove-DuoGroup.ps1' 36 #Region './Public/Admin API/Groups/Update-DuoGroup.ps1' 0 function Update-DuoGroup { <# .SYNOPSIS Update Group .DESCRIPTION Update information about a group. Requires "Grant write resource" API permission. .PARAMETER GroupId Group Id to update .PARAMETER Name Update the name of the group. .PARAMETER Description Update the description of the group. .PARAMETER Status The authentication status of the group. .EXAMPLE Update-DuoGroup -GroupId 'SOMEDUOID' -Status 'Disabled' .LINK https://duo.com/docs/adminapi#update-group #> [CmdletBinding(SupportsShouldProcess)] Param( [Parameter(ValueFromPipelineByPropertyName = $true, Mandatory = $true)] [Alias('group_id')] [string]$GroupId, [Parameter()] [string]$Name, [Parameter()] [string]$Description, [Parameter()] [ValidateSet('Active', 'Bypass', 'Disabled')] [string]$Status ) process { $Params = @{} if ($Name) { $Params.name = $Name } if ($Description) { $Params.desc = $Description } if ($Status) { $Params.status = $Status.ToLower() } $DuoRequest = @{ Method = 'POST' Path = '/admin/v1/groups/{0}' -f $GroupId Params = $Params } if ($PSCmdlet.ShouldProcess($GroupId)) { $Request = Invoke-DuoRequest @DuoRequest if ($Request.stat -ne 'OK') { $Request } else { $Request.response } } } } #EndRegion './Public/Admin API/Groups/Update-DuoGroup.ps1' 67 #Region './Public/Admin API/Bypass Codes/Get-DuoBypassCodes.ps1' 0 function Get-DuoBypassCodes { <# .SYNOPSIS Retrieve Bypass Codes .DESCRIPTION Returns information about a single bypass code or a paged list of information about all bypass codes. Output does not include the actual bypass codes. Requires "Grant read resource" API permission. .PARAMETER BypassCodeId Bypass Code Id to retrieve .EXAMPLE Get-DuoBypassCodes .LINK https://duo.com/docs/adminapi#retrieve-bypass-codes .LINK https://duo.com/docs/adminapi#retrieve-bypass-code-by-id #> [CmdletBinding()] Param( [Parameter(ValueFromPipelineByPropertyName = $true)] [Alias('bypass_code_id')] [string]$BypassCodeId ) process { if ($BypassCodeId) { $Path = '/admin/v1/bypass_codes/{0}' -f $BypassCodeId } else { $Path = '/admin/v1/bypass_codes' } $DuoRequest = @{ Method = 'GET' Path = $Path } if ($BypassCodeId) { $Request = Invoke-DuoRequest @DuoRequest if ($Request.stat -ne 'OK') { $Request } else { $Request.response } } else { Invoke-DuoPaginatedRequest -DuoRequest $DuoRequest } } } Set-Alias -Name Get-DuoBypassCode -Value Get-DuoBypassCodes #EndRegion './Public/Admin API/Bypass Codes/Get-DuoBypassCodes.ps1' 55 #Region './Public/Admin API/Bypass Codes/Remove-DuoBypassCode.ps1' 0 function Remove-DuoBypassCode { <# .SYNOPSIS Delete Bypass Code .DESCRIPTION Delete the bypass code with ID bypass_code_id from the system. Requires "Grant write resource" API permission. .PARAMETER BypassCodeId ID of bypass code to remove .EXAMPLE Remove-DuoBypassCode -BypassCodeId SOMEDUOID .LINK https://duo.com/docs/adminapi#delete-bypass-code #> [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')] Param( [Parameter(ValueFromPipelineByPropertyName = $true, Mandatory = $true)] [Alias('bypass_code_id')] [string]$BypassCodeId ) process { $DuoRequest = @{ method = 'DELETE' path = '/admin/v1/bypass_codes/{0}' -f $BypassCodeId } if ($PSCmdlet.ShouldProcess($BypassCodeId)) { Invoke-DuoRequest @DuoRequest } } } #EndRegion './Public/Admin API/Bypass Codes/Remove-DuoBypassCode.ps1' 35 #Region './Public/Admin API/Trust Monitor/Get-DuoTrustMonitorEvents.ps1' 0 function Get-DuoTrustMonitorEvents { <# .SYNOPSIS Retrieve Trust Monitor Events .DESCRIPTION Returns a paged list of events surfaced by Trust Monitor from the last 180 days. To fetch all results, call repeatedly with the next_offset paging parameter as long as the result metadata has next_offset values. Requires "Grant read log" API permission. .PARAMETER Days Number of days to retrieve with max time of now .PARAMETER Hours Number of hours to retrieve with max time of now .PARAMETER StartDate The start date for log entries .PARAMETER EndDate The end date for log enties .PARAMETER MinTime Return records that have a 13 character Unix timestamp in milliseconds of mintime or later. This value must be strictly less then maxtime. .PARAMETER MaxTime Return records that have a 13 character Unix timestamp in milliseconds of maxtime or earlier. This value must be strictly greater then mintime. .LINK https://duo.com/docs/adminapi#retrieve-events .EXAMPLE Get-DuoTrustMonitorEvents -Days 30 .NOTES We recommend requesting Trust Monitor events no more than once per minute. #> [CmdletBinding()] Param( [Parameter(Mandatory=$true,ParameterSetName='Days')] [int]$Days, [Parameter(Mandatory = $true, ParameterSetName = 'Hours')] [int]$Hours, [Parameter(Mandatory=$true,ParameterSetName='MinMaxTime')] [string]$MinTime, [Parameter(Mandatory = $true, ParameterSetName = 'MinMaxTime')] [string]$MaxTime, [Parameter(Mandatory = $true, ParameterSetName = 'DateTime')] [datetime]$StartDate, [Parameter(ParameterSetName = 'DateTime')] [datetime]$EndDate, [Parameter()] [ValidateSet('auth','bypass_status_enabled')] [string]$Type ) if ($Days) { $MaxTime = [int64](Get-Date -UFormat '%s000') $MinTime = $MaxTime - [int64](86400000 * $Days) } if ($Hours) { $MaxTime = [int64](Get-Date -UFormat '%s000') $MinTime = $MaxTime - [int64](3600000 * $Hours) } if ($StartDate) { if ($EndDate) { $MaxTime = $EndDate | Get-Date -UFormat '%s000' } else { $MaxTime = Get-Date -UFormat '%s000' } $MinTime = $StartDate | Get-Date -UFormat '%s000' } $Params = @{ mintime = $MinTime.ToString() maxtime = $MaxTime.ToString() } if ($types) { $Params.types = $Types } $DuoRequest = @{ Method = 'GET' Path = '/admin/v1/trust_monitor/events' Params = $Params } $Response = Invoke-DuoRequest @DuoRequest if ($Response.stat -eq 'OK') { $Response.response.events } else { $Response } } #EndRegion './Public/Admin API/Trust Monitor/Get-DuoTrustMonitorEvents.ps1' 103 #Region './Public/Admin API/Info/Get-DuoAccountSummary.ps1' 0 function Get-DuoAccountSummary { <# .SYNOPSIS Retrieve Summary .DESCRIPTION Returns a summary of account utilization information. Requires "Grant read information" API permission. .EXAMPLE Get-DuoAccountSummary .LINK https://duo.com/docs/adminapi#retrieve-summary #> [CmdletBinding()] Param() $DuoRequest = @{ Method = 'GET' Path = '/admin/v1/info/summary' } $Request = Invoke-DuoRequest @DuoRequest if ($Request.stat -ne 'OK') { $Request } else { $Request.response } } #EndRegion './Public/Admin API/Info/Get-DuoAccountSummary.ps1' 33 #Region './Public/Admin API/Info/Get-DuoAuthenticationAttempts.ps1' 0 function Get-DuoAuthenticationAttempts { <# .SYNOPSIS Authentication Attempts Report .DESCRIPTION Retrieve counts of authentication attempts for a given time period (not to exceed 180 days), broken down by result. Requires "Grant read information" API permission. .PARAMETER MaxTime Limit report to events before this Unix timestamp. Defaults to the current time. .PARAMETER MinTime Limit report to events after this Unix timestamp. Defaults to thirty days before maxtime. .EXAMPLE Get-DuoAuthenticationAttempts .LINK https://duo.com/docs/adminapi#authentication-attempts-report #> [CmdletBinding()] Param( [Parameter()] [string]$MaxTime, [Parameter()] [string]$MinTime ) $Params = @{} if ($MaxTime) { $Params.maxtime = $MaxTime } if ($MinTime) { $Params.mintime = $MinTime } $DuoRequest = @{ Method = 'GET' Path = '/admin/v1/info/authentication_attempts' Params = $Params } $Request = Invoke-DuoRequest @DuoRequest if ($Request.stat -ne 'OK') { $Request } else { $Request.response } } #EndRegion './Public/Admin API/Info/Get-DuoAuthenticationAttempts.ps1' 50 #Region './Public/Admin API/Info/Get-DuoTelephonyCreditsUsed.ps1' 0 function Get-DuoTelephonyCreditsUsed { <# .SYNOPSIS Authentication Attempts Report .DESCRIPTION Retrieve counts of authentication attempts for a given time period (not to exceed 180 days), broken down by result. Requires "Grant read information" API permission. .PARAMETER MaxTime Limit report to events before this Unix timestamp. Defaults to the current time. .PARAMETER MinTime Limit report to events after this Unix timestamp. Defaults to thirty days before maxtime. .EXAMPLE Get-DuoTelephonyCreditsUsed .LINK https://duo.com/docs/adminapi#telephony-credits-used-report #> [CmdletBinding()] Param( [Parameter()] [string]$MaxTime, [Parameter()] [string]$MinTime ) $Params = @{} if ($MaxTime) { $Params.maxtime = $MaxTime } if ($MinTime) { $Params.mintime = $MinTime } $DuoRequest = @{ Method = 'GET' Path = '/admin/v1/info/telephony_credits_used' Params = $Params } $Request = Invoke-DuoRequest @DuoRequest if ($Request.stat -ne 'OK') { $Request } else { $Request.response } } #EndRegion './Public/Admin API/Info/Get-DuoTelephonyCreditsUsed.ps1' 50 #Region './Public/Admin API/Info/Get-DuoUserAuthenticationAttempts.ps1' 0 function Get-DuoUserAuthenticationAttempts { <# .SYNOPSIS Users with Authentication Attempts Report .DESCRIPTION Retrieve counts of users with authentication attempts for a given time period (not to exceed 180 days), broken down by result. Each count is the number of users who had at least one authentication attempt ending with that result. Requires "Grant read information" API permission. .PARAMETER MaxTime Limit report to events before this Unix timestamp. Defaults to the current time. .PARAMETER MinTime Limit report to events after this Unix timestamp. Defaults to thirty days before maxtime. .EXAMPLE Get-DuoUserAuthenticationAttempts .LINK https://duo.com/docs/adminapi#users-with-authentication-attempts-report #> [CmdletBinding()] Param( [Parameter()] [string]$MaxTime, [Parameter()] [string]$MinTime ) $Params = @{} if ($MaxTime) { $Params.maxtime = $MaxTime } if ($MinTime) { $Params.mintime = $MinTime } $DuoRequest = @{ Method = 'GET' Path = '/admin/v1/info/user_authentication_attempts' Params = $Params } $Request = Invoke-DuoRequest @DuoRequest if ($Request.stat -ne 'OK') { $Request } else { $Request.response } } #EndRegion './Public/Admin API/Info/Get-DuoUserAuthenticationAttempts.ps1' 50 #Region './Public/Admin API/Settings/Get-DuoSetting.ps1' 0 function Get-DuoSetting { <# .SYNOPSIS Retrieve Settings .DESCRIPTION Returns global Duo settings. These settings can also be viewed and set in the Duo Admin Panel. Requires "Grant settings" API permission. .EXAMPLE Get-DuoSettings .LINK https://duo.com/docs/adminapi#retrieve-settings #> [CmdletBinding()] Param() $DuoRequest = @{ Method = 'GET' Path = '/admin/v1/settings' } $Request = Invoke-DuoRequest @DuoRequest if ($Request.stat -ne 'OK') { $Request } else { $Request.response } } Set-Alias -Name Get-DuoSettings -Value Get-DuoSetting #EndRegion './Public/Admin API/Settings/Get-DuoSetting.ps1' 33 #Region './Public/Admin API/Settings/Update-DuoSetting.ps1' 0 function Update-DuoSetting { <# .SYNOPSIS Modify Settings .DESCRIPTION Change global Duo settings. Requires "Grant settings" API permission. .PARAMETER CallerId Automated calls will appear to come from this number. This does not apply to text messages. Customizing this number may cause telephony providers to flag your number as fraudulent and result in failed user authentications. .PARAMETER FraudEmail The email address to be notified when a user reports a fraudulent authentication attempt or is locked out due to failed authentication attempts, or empty for all administrators will be notified. If fraud_email is set to a specific email address and fraud_email_enabled is set to false, the specific email address value is cleared. .PARAMETER FraudEmailEnabled Set to true to enable fraudulent authentication notification emails. False disables the fraud email functionality. If fraud_email is set to a specific email address and fraud_email_enabled is set to false, the specific email address value is cleared. .PARAMETER HelpdeskBypass Grants permission for administrators with the Help Desk role to generate bypass codes for users. The default value allow permits unrestricted generation of bypass codes, limit plus a value for helpdesk_bypass_expiration allows Help Desk admins to generate bypass codes with a preset expirtation, and deny prevents Help Desk admins from generating any bypass codes. .PARAMETER HelpdeskBypassExpiration Integer specifying a default expiration for bypass codes generated by Help Desk admins, in minutes. If not set, Help Desk admins may change bypass code expiration from the default 60 minutes after creation if helpdesk_bypass is set to allow. If specifying a value, also set helpdesk_bypass to limit. .PARAMETER HelpdeskCanSendEnrollEmail Permits Help Desk administrators to send or resend enrollment emails to users. Set to true to allow sending of enrollment emails. Default value is false. .PARAMETER InactiveUserExpiration Users will be automatically deleted if they are inactive (no successful logins) for this number of days. Minimum: 30 Maximum: 365 .PARAMETER KeypressConfirm The key for users to press to authenticate, or empty if any key should be pressed to authenticate. If this is empty, keypress_fraud must be as well. .PARAMETER KeypressFraud The key for users to report fraud, or empty if any key should be pressed to authenticate. If this is empty, keypress_confirm must be as well. .PARAMETER Language ets the language used in the browser-based user authentication prompt. One of: "EN", "DE", "FR". Default: "EN" .PARAMETER LockoutExpireDuration If non-zero, the time in minutes until a locked-out user's status reverts to "Active". If 0, a user remains locked out until their status is manually changed (By an admin or API call). Minimum: 5 Maximum: 30000 .PARAMETER LockoutThreshold The number of consecutive failed authentication attempts before the user's status is set to "Locked Out" and the user is denied access. Default is 10 attempts. Minimum: 1 Maximum: 9999 .PARAMETER MinimumPasswordLength The minimum number of characters that an administrator's Duo Admin Panel password must contain. This is only enforced on password creation and reset; existing passwords will not be invalidated. Default: 12. Minimum: 12 Maximum: 100 .PARAMETER PasswordRequiresLowerAlpha If true, administrator passwords will be required to contain a lower case alphabetic character. If false, administrator passwords will not be required to contain a lower case alphabetic character. This is only enforced on password creation and reset; existing passwords will not be invalidated. Default: false. .PARAMETER PasswordRequiresNumeric If true, administrator passwords will be required to contain a numeric character. If false, administrator passwords will not be required to contain a numeric character. This is only enforced on password creation and reset; existing passwords will not be invalidated. Default: false. .PARAMETER PasswordRequiresSpecial If true, administrator passwords will be required to contain a special (non-alphanumeric) character. If false, administrator passwords will not be required to contain a special (non-alphanumeric) character. This is only enforced on password creation and reset; existing passwords will not be invalidated. Default: false. .PARAMETER PasswordRequiresUpperAlpha If true, administrator passwords will be required to contain an upper case alphabetic character. If false, administrator passwords will not be required to contain an upper case alphabetic character. This is only enforced on password creation and reset; existing passwords will not be invalidated. Default: false. .PARAMETER SmsBatch The number of passcodes to send at one time, up to 10. .PARAMETER SmsExpiration The time in minutes to expire and invalidate SMS passcodes, or empty if they should not expire. .PARAMETER SmsMessage Description sent with every batch of SMS passcodes. .PARAMETER SmsRefresh If 1, a new set of SMS passcodes will automatically be sent after the last one is used. If 0, a new set will not be sent. .PARAMETER TelephonyWarningMin Configure a alert to be sent when the account has fewer than this many telephony credits remaining. .PARAMETER Timezone This is the timezone used when displaying timestamps in the Duo Admin Panel. Timezones must be entries in the IANA Time Zone Database, for example, "US/Eastern", "Australia/Darwin", "GMT". .PARAMETER UserManagersCanPutUsersInBypass Permits User Manager administrators to apply "Bypass" status to users. Set to false to prevent User Managers from applying "Bypass" status. Default value is true. .PARAMETER UserTelephonyCostMax The maximum number of telephony credits a user may consume in a single authentication event. This excludes Duo administrators authenticating to the Duo administration panel. If you know the countries from which your users expect to authenticate with phone callback we recommend adjusting this down from the default to match the most expensive expected country to help avoid misuse, using the values from the Telephony Credits documentation. Default: 20. .EXAMPLE Update-DuoSetting -FraudEmail helpdesk@domain.com .LINK https://duo.com/docs/adminapi#modify-settings .INPUTS None .OUTPUTS PSCustomObject. Duo Settings object .NOTES #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingUsernameAndPasswordParams', '')] [CmdletBinding(SupportsShouldProcess)] Param( [Parameter()] [string]$CallerId, [Parameter()] [string]$FraudEmail, [Parameter()] [switch]$FraudEmailEnabled, [Parameter()] [ValidateSet('Allow', 'Limit', 'Deny')] [string]$HelpdeskBypass, [Parameter()] [int]$HelpdeskBypassExpiration, [Parameter()] [switch]$HelpdeskCanSendEnrollEmail, [Parameter()] [ValidateRange(30, 365)] [int]$InactiveUserExpiration, [Parameter()] [string]$KeypressConfirm, [Parameter()] [string]$KeypressFraud, [Parameter()] [ValidateSet('EN', 'DE', 'FR')] [string]$Language, [Parameter()] [ValidateRange(5, 30000)] [int]$LockoutExpireDuration, [Parameter()] [ValidateRange(1, 9999)] [int]$LockoutThreshold, [Parameter()] [ValidateRange(12, 100)] [int]$MinimumPasswordLength, [Parameter()] [switch]$PasswordRequiresLowerAlpha, [Parameter()] [switch]$PasswordRequiresNumeric, [Parameter()] [switch]$PasswordRequiresSpecial, [Parameter()] [switch]$PasswordRequiresUpperAlpha, [Parameter()] [ValidateRange(1, 10)] [int]$SmsBatch, [Parameter()] [int]$SmsExpiration, [Parameter()] [string]$SmsMessage, [Parameter()] [ValidateRange(0, 1)] [int]$SmsRefresh, [Parameter()] [int]$TelephonyWarningMin, [Parameter()] [string]$Timezone, [Parameter()] [switch]$UserManagersCanPutUsersInBypass, [Parameter()] [int]$UserTelephonyCostMax ) $Params = @{} if ($CallerId) { $Params.caller_id = $CallerId } if ($FraudEmail) { $Params.fraud_email = $FraudEmail } if ($FraudEmailEnabled.IsPresent) { $Params.fraud_email_enabled = $FraudEmailEnabled.IsPresent } if ($HelpdeskBypass) { $Params.helpdesk_bypass = $HelpdeskBypass.ToLower() } if ($HelpdeskBypassExpiration) { $Params.helpdesk_bypass_expiration = $HelpdeskBypassExpiration } if ($HelpdeskCanSendEnrollEmail.IsPresent) { $Params.helpdesk_can_send_enroll_email = $HelpdeskCanSendEnrollEmail.IsPresent } if ($HelpdeskMessage) { $Params.helpdesk_message = $HelpdeskMessage } if ($InactiveUserExpiration) { $Params.inactive_user_expiration = $InactiveUserExpiration } if ($KeypressConfirm) { $Params.keypress_confirm = $KeypressConfirm } if ($KeypressFraud) { $Params.keypress_fraud = $KeypressFraud } if ($Language) { $Params.language = $Language } if ($LockoutExpireDuration) { $Params.lockout_expire_duration = $LockoutExpireDuration } if ($LockoutThreshold) { $Params.lockout_threshold = $LockoutThreshold } if ($MinimumPasswordLength) { $Params.minimum_password_length = $MinimumPasswordLength } if ($PasswordRequiresLowerAlpha.IsPresent) { $Params.password_requires_lower_alpha = $PasswordRequiresLowerAlpha.IsPresent } if ($PasswordRequiresNumeric.IsPresent) { $Params.password_requires_numeric = $PasswordRequiresNumeric.IsPresent } if ($PasswordRequiresSpecial.IsPresent) { $Params.password_requires_special = $PasswordRequiresSpecial.IsPresent } if ($PasswordRequiresUpperAlpha.IsPresent) { $Params.password_requires_upper_alpha = $PasswordRequiresUpperAlpha.IsPresent } if ($SmsBatch) { $Params.sms_batch = $SmsBatch } if ($SmsExpiration) { $Params.sms_expiration = $SmsExpiration } if ($SmsMessage) { $Params.sms_message = $SmsMessage } if ($SmsRefresh) { $Params.sms_refresh = $SmsRefresh } if ($TelephonyWarningMin) { $Params.telephony_warning_min = $TelephonyWarningMin } if ($Timezone) { $Params.timezone = $Timezone } if ($UserManagersCanPutUsersInBypass.IsPresent) { $Params.user_managers_can_put_users_in_bypass = $UserManagersCanPutUsersInBypass } if ($UserTelephonyCostMax) { $Params.user_telephony_cost_max = $UserTelephonyCostMax } $DuoRequest = @{ Method = 'POST' Path = '/admin/v1/settings' Params = $Params } if ($PSCmdlet.ShouldProcess('Duo Account Settings')) { $Request = Invoke-DuoRequest @DuoRequest if ($Request.stat -ne 'OK') { $Request } else { $Request.response } } } Set-Alias -Name Update-DuoSettings -Value Update-DuoSetting #EndRegion './Public/Admin API/Settings/Update-DuoSetting.ps1' 232 #Region './Public/Admin API/Phones/Get-DuoPhone.ps1' 0 function Get-DuoPhone { <# .SYNOPSIS Retrieve Phones .DESCRIPTION Returns a single phone or a paged list of phones. If no number or extension parameters are provided, the list will contain all phones. Otherwise, the list will contain either single phone (if a match was found), or no phones. Requires "Grant read resource" API permission. .PARAMETER PhoneId Id of phone .PARAMETER Number Specify a phone number in E.164 format to look up a single phone. .PARAMETER Extension The extension, if necessary. .EXAMPLE Get-DuoPhones .LINK https://duo.com/docs/adminapi#retrieve-phones .LINK https://duo.com/docs/adminapi#retrieve-phone-by-id #> [CmdletBinding()] Param( [Parameter(ValueFromPipelineByPropertyName = $true)] [Alias('phone_id')] [string]$PhoneId, [Parameter()] [string]$Number, [Parameter()] [int]$Extension ) process { if ($GroupId) { $Path = '/admin/v2/phones/{0}' -f $PhoneId } else { $Path = '/admin/v1/phones' $Params = @{} if ($Number) { $Params.number = $Number } if ($Extension) { $Params.extension = $Extension } } $DuoRequest = @{ Method = 'GET' Path = $Path } if ($Params) { $DuoRequest.Params = $Params Invoke-DuoPaginatedRequest -DuoRequest $DuoRequest } else { $Request = Invoke-DuoRequest @DuoRequest if ($Request.stat -ne 'OK') { $Request } else { $Request.response } } } } Set-Alias -Name Get-DuoPhones -Value Get-DuoPhone #EndRegion './Public/Admin API/Phones/Get-DuoPhone.ps1' 70 #Region './Public/Admin API/Phones/New-DuoPhone.ps1' 0 function New-DuoPhone { <# .SYNOPSIS Create Phone .DESCRIPTION Create a new phone with a specified phone number or other parameters. Requires "Grant write resource" API permission. .PARAMETER Number The phone number; E.164 format recommended (i.e. "+17345551212"). If no leading plus sign is provided then it is assumed to be a United States number and an implicit "+1" country code is prepended. Dashes and spaces are ignored. A phone with a smartphone platform but no number is a tablet. .PARAMETER Name Free-form label for the phone. .PARAMETER Extension The extension. .PARAMETER Type The phone type. See Retrieve Phones for a list of possible values. .PARAMETER Platform The phone platform. See Retrieve Phones for a list of possible values. .PARAMETER PostDelay The time (in seconds) to wait after the extension is dialed and before the speaking the prompt. .PARAMETER PreDelay The time (in seconds) to wait after the number picks up and before dialing the extension. .EXAMPLE New-DuoPhone -Name 'TestPhone -Number '+15558675309' -Type Mobile -Platform 'Apple iOS' .LINK https://duo.com/docs/adminapi#create-phone #> [CmdletBinding(SupportsShouldProcess)] Param( [Parameter()] [string]$Number, [Parameter()] [string]$Name, [Parameter()] [string]$Extension, [Parameter()] [ValidateSet('Unknown', 'Mobile', 'Landline')] [string]$Type = 'Mobile', [Parameter()] [ValidateSet('Unknown', 'Google Android', 'Apple iOS', 'Windows Phone', 'RIM Blackberry', 'Java J2me', 'Palm WebOS', 'Symbian OS', 'Windows Mobile', 'Generic Smartphone')] [string]$Platform = 'Generic Smartphone', [Parameter()] [int]$PostDelay, [Parameter()] [int]$PreDelay ) $Params = @{} if ($Number) { $Params.number = $Number } if ($Name) { $Params.name = $Name } if ($Extension) { $Params.extension = $Extension } if ($Type) { $Params.type = $Type.ToLower() } if ($Platform) { $Params.platform = $Platform.ToLower() } if ($PreDelay) { $Params.predelay = $PreDelay } if ($PostDelay) { $Params.postdelay = $PostDelay } $DuoRequest = @{ Method = 'POST' Path = '/admin/v1/phones' Params = $Params } if ($PSCmdlet.ShouldProcess("$Type - $Platform")) { $Request = Invoke-DuoRequest @DuoRequest if ($Request.stat -ne 'OK') { $Request } else { $Request.response } } } #EndRegion './Public/Admin API/Phones/New-DuoPhone.ps1' 89 #Region './Public/Admin API/Phones/New-DuoPhoneActivationCode.ps1' 0 function New-DuoPhoneActivationCode { <# .SYNOPSIS Create Activation Code .DESCRIPTION Generate a Duo Mobile activation code. This method will fail if the phone's type or platform are Unknown. Requires "Grant write resource" API permission. .PARAMETER PhoneId Id of phone .PARAMETER ValidSecs The number of seconds this activation code remains valid. Default: 86400 (one day). Expiration not supported for legacy phone platforms that support passcode generation only (not Duo Push). .PARAMETER Install Specify 1 to also return an installation URL for Duo Mobile; 0 to not return a URL. Default: 0. .EXAMPLE New-DuoPhoneActivationCode -PhoneId SOMEPHONEID .LINK https://duo.com/docs/adminapi#create-activation-code #> [CmdletBinding(SupportsShouldProcess)] Param( [Parameter(ValueFromPipelineByPropertyName = $true, Mandatory = $true)] [Alias('phone_id')] [string]$PhoneId, [Parameter()] [int]$ValidSecs = 86400, [Parameter()] [int]$Install = 0 ) process { $Params = @{} if ($ValidSecs) { $Params.valid_secs = $ValidSecs } if ($Install) { $Params.install = $Install } $DuoRequest = @{ Method = 'POST' Path = '/admin/v1/phones/{0}/activation_url' -f $PhoneId Params = $Params } if ($PSCmdlet.ShouldProcess($PhoneId)) { $Request = Invoke-DuoRequest @DuoRequest if ($Request.stat -ne 'OK') { $Request } else { $Request.response } } } } #EndRegion './Public/Admin API/Phones/New-DuoPhoneActivationCode.ps1' 59 #Region './Public/Admin API/Phones/Remove-DuoPhone.ps1' 0 function Remove-DuoPhone { <# .SYNOPSIS Delete Phone .DESCRIPTION Delete the phone with ID phone_id from the system. Requires "Grant write resource" API permission. .PARAMETER PhoneId Id of phone .EXAMPLE Remove-DuoPhone -PhoneId SOMEDUOID .LINK https://duo.com/docs/adminapi#delete-phone #> [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')] Param( [Parameter(ValueFromPipelineByPropertyName = $true, Mandatory = $true)] [Alias('phone_id')] [string]$PhoneId ) process { $DuoRequest = @{ method = 'DELETE' path = '/admin/v1/phones/{0}' -f $PhoneId } if ($PSCmdlet.ShouldProcess($PhoneId)) { Invoke-DuoRequest @DuoRequest } } } #EndRegion './Public/Admin API/Phones/Remove-DuoPhone.ps1' 36 #Region './Public/Admin API/Phones/Send-DuoPhoneActivationSms.ps1' 0 function Send-DuoPhoneActivationSms { <# .SYNOPSIS Send Activation Code via SMS .DESCRIPTION Generate a Duo Mobile activation code and send it to the phone via SMS, optionally sending an additional message with a URL to install Duo Mobile. This method will fail if the phone's type or platform are Unknown. Requires "Grant write resource" API permission. .PARAMETER PhoneId Parameter description .PARAMETER ValidSecs The number of seconds this activation code remains valid. Default: 86400 (one day). .PARAMETER Install Specify 1 to cause an installation SMS message to be sent before the activation message, or 0 to not send an installation SMS message. Default: 0. .PARAMETER InstallationMsg A custom installation message to send to the user. Only valid if installation was requested. Must contain the phrase "<insturl>", which is replaced with the installation URL. .PARAMETER ActivationMsg A custom activation message to send to the user. Must contain "<acturl>", which is replaced with the activation URL. .EXAMPLE Send-DuoPhoneActivationSms -PhoneId SOMEDUOID -ValidSecs 3600 -Install 1 .LINK https://duo.com/docs/adminapi#send-activation-code-via-sms .NOTES SMS Size Limits The recommended maximum length for activation_msg and installation_msg is 80 characters. Activation and installation SMS messages are limited to 160 characters or less. If providing custom text, please make sure to leave enough room for a URL to be sent in the same message. The exact length available for custom text varies depending on the device's platform and whether international characters were used. Activation URLs are typically about 60 characters long. Installation URLs are between 50 and 75 characters long. #> [CmdletBinding()] Param( [Parameter(ValueFromPipelineByPropertyName = $true, Mandatory = $true)] [Alias('phone_id')] [string]$PhoneId, [Parameter()] [int]$ValidSecs = 86400, [Parameter()] [int]$Install = 0, [Parameter()] [string]$InstallationMsg, [Parameter()] [string]$ActivationMsg ) process { $Params = @{} if ($ValidSecs) { $Params.valid_secs = $ValidSecs } if ($Install) { $Params.install = $Install if ($InstallationMsg) { if ($InstallationMsg -notmatch '<insturl>') { Write-Error 'Installation message must contain <insturl>' return $false } $Params.installation_msg = $InstallationMsg } } if ($ActivationMsg) { if ($ActivationMsg -notmatch '<acturl>') { Write-Error 'Activation message must contain <acturl>' return $false } $Params.activation_msg = $ActivationMsg } $DuoRequest = @{ Method = 'POST' Path = '/admin/v1/phones/{0}/send_sms_activation' -f $PhoneId Params = $Params } $Request = Invoke-DuoRequest @DuoRequest if ($Request.stat -ne 'OK') { $Request } else { $Request.response } } } #EndRegion './Public/Admin API/Phones/Send-DuoPhoneActivationSms.ps1' 90 #Region './Public/Admin API/Phones/Send-DuoPhoneInstallationSms.ps1' 0 function Send-DuoPhoneInstallationSms { <# .SYNOPSIS Send Installation URL via SMS .DESCRIPTION Send a message via SMS describing how to install Duo Mobile. This method will fail if the phone's type or platform are Unknown. Requires "Grant write resource" API permission. .PARAMETER PhoneId Id of phone .PARAMETER InstallationMsg A custom installation message to send to the user. Must contain the phrase "<insturl>", which is replaced with the installation URL. .EXAMPLE Send-DuoPhoneInstallationSms -PhoneId SOMEDUOID -InstallationMsg 'Install Duo Mobile! <insturl> - Your friendly IT department' .LINK https://duo.com/docs/adminapi#send-installation-url-via-sms .NOTES SMS Size Limits The recommended maximum length for installation_msg is 80 characters. Installation SMS messages are limited to 160 characters or less. If providing custom text, please make sure to leave enough room for a URL to be sent in the same message. The exact length available for custom text varies depending on the device's platform and whether international characters were used. Installation URLs are between 50 and 75 characters long. #> [CmdletBinding()] Param( [Parameter(ValueFromPipelineByPropertyName = $true, Mandatory = $true)] [Alias('phone_id')] [string]$PhoneId, [Parameter()] [string]$InstallationMsg ) process { $Params = @{} if ($InstallationMsg) { if ($InstallationMsg -notmatch '<insturl>') { Write-Error 'Installation message must contain <insturl>' return $false } $Params.installation_msg = $InstallationMsg } $DuoRequest = @{ Method = 'POST' Path = '/admin/v1/phones/{0}/send_sms_installation' -f $PhoneId Params = $Params } $Request = Invoke-DuoRequest @DuoRequest if ($Request.stat -ne 'OK') { $Request } else { $Request.response } } } #EndRegion './Public/Admin API/Phones/Send-DuoPhoneInstallationSms.ps1' 61 #Region './Public/Admin API/Phones/Send-DuoPhoneSmsPasscode.ps1' 0 function Send-DuoPhoneSmsPasscode { <# .SYNOPSIS Send Passcodes via SMS .DESCRIPTION Generate a new batch of SMS passcodes send them to the phone in a single SMS message. Requires "Grant write resource" API permission. .PARAMETER PhoneId Id of phone .EXAMPLE Send-DuoPhoneSmsPasscode -PhoneId SOMEDUOID .LINK https://duo.com/docs/adminapi#send-passcodes-via-sms #> [CmdletBinding()] Param( [Parameter(ValueFromPipelineByPropertyName = $true, Mandatory = $true)] [Alias('phone_id')] [string]$PhoneId ) process { $DuoRequest = @{ Method = 'POST' Path = '/admin/v1/phones/{0}/send_sms_passcodes' -f $PhoneId } $Request = Invoke-DuoRequest @DuoRequest if ($Request.stat -ne 'OK') { $Request } else { $Request.response } } } Set-Alias -Name Send-DuoPhoneSmsPasscodes -Value Send-DuoPhoneSmsPasscode #EndRegion './Public/Admin API/Phones/Send-DuoPhoneSmsPasscode.ps1' 42 #Region './Public/Admin API/Phones/Update-DuoPhone.ps1' 0 function Update-DuoPhone { <# .SYNOPSIS Modify Phone .DESCRIPTION Change the details of the phone with ID phone_id. Requires "Grant write resource" API permission. .PARAMETER PhoneId Id of phone .PARAMETER Number The new phone number; E.164 format recommended (i.e. "+17345551212"). If no leading plus sign is provided then it is assumed to be a United States number and an implicit "+1" country code is prepended. Dashes and spaces are ignored. .PARAMETER Name Free-form label for the phone. .PARAMETER Extension The new extension. .PARAMETER Type The phone type. See Retrieve Phones for a list of possible values. .PARAMETER Platform The phone platform. See Retrieve Phones for a list of possible values.The time (in seconds) to wait after the number picks up and before dialing the extension. .PARAMETER PostDelay The time (in seconds) to wait after the extension is dialed and before the speaking the prompt. .PARAMETER PreDelay The time (in seconds) to wait after the number picks up and before dialing the extension. .EXAMPLE Update-DuoPhone -PhoneId SOMEDUOID -Name 'New phone name' .LINK https://duo.com/docs/adminapi#modify-phone #> [CmdletBinding(SupportsShouldProcess)] Param( [Parameter(ValueFromPipelineByPropertyName = $true, Mandatory = $true)] [Alias('phone_id')] [string]$PhoneId, [Parameter()] [string]$Number, [Parameter()] [string]$Name, [Parameter()] [string]$Extension, [Parameter()] [ValidateSet('Unknown', 'Mobile', 'Landline')] [string]$Type = 'Mobile', [Parameter()] [ValidateSet('Unknown', 'Google Android', 'Apple iOS', 'Windows Phone', 'RIM Blackberry', 'Java J2me', 'Palm WebOS', 'Symbian OS', 'Windows Mobile', 'Generic Smartphone')] [string]$Platform = 'Generic Smartphone', [Parameter()] [int]$PostDelay, [Parameter()] [int]$PreDelay ) process { $Params = @{} if ($Number) { $Params.number = $Number } if ($Name) { $Params.name = $Name } if ($Extension) { $Params.extension = $Extension } if ($Type) { $Params.type = $Type.ToLower() } if ($Platform) { $Params.platform = $Platform.ToLower() } if ($PreDelay) { $Params.predelay = $PreDelay } if ($PostDelay) { $Params.postdelay = $PostDelay } $DuoRequest = @{ Method = 'POST' Path = '/admin/v1/phones/{0}' -f $PhoneId Params = $Params } if ($PSCmdlet.ShouldProcess($PhoneId)) { $Request = Invoke-DuoRequest @DuoRequest if ($Request.stat -ne 'OK') { $Request } else { $Request.response } } } } #EndRegion './Public/Admin API/Phones/Update-DuoPhone.ps1' 96 #Region './Public/Auth API/Get-DuoAuthEnrollmentStatus.ps1' 0 function Get-DuoAuthEnrollmentStatus { <# .SYNOPSIS Duo Auth Enrollment Status .DESCRIPTION Check whether a user has completed enrollment. .PARAMETER UserId ID of the user. .PARAMETER ActivationCode Activation code, as returned from /enroll. .EXAMPLE Get-DuoAuthEnrollmentStatus -UserId SOMEUSERID -ActivationCode SOMEACTIVATIONCODE .LINK https://duo.com/docs/authapi#/enroll_status .NOTES #> [CmdletBinding()] Param( [Parameter(ValueFromPipelineByPropertyName = $true, Mandatory = $true)] [Alias('user_id')] [string]$UserId, [Parameter(ValueFromPipelineByPropertyName = $true, Mandatory = $true)] [Alias('activation_code')] [string]$ActivationCode ) process { $Params = @{ user_id = $UserId activation_code = $ActivationCode } $DuoRequest = @{ Method = 'POST' Path = '/auth/v2/enroll_status' Params = $Params } $Response = Invoke-DuoRequest @DuoRequest if ($Response.stat -eq 'OK') { $Response.response } else { $Response } } } #EndRegion './Public/Auth API/Get-DuoAuthEnrollmentStatus.ps1' 55 #Region './Public/Auth API/Get-DuoAuthLogo.ps1' 0 function Get-DuoAuthLogo { <# .SYNOPSIS Duo Auth Logo .DESCRIPTION The /logo endpoint provides a programmatic way to retrieve your stored logo. .PARAMETER FilePath Where to save the logo .EXAMPLE Get-DuoAuthLogo -FilePath ./logo.png .LINK https://duo.com/docs/authapi#/logo .NOTES #> [CmdletBinding()] Param( [Parameter(Mandatory = $true)] [string]$FilePath ) process { $DuoRequest = @{ Method = 'GET' Path = '/auth/v2/logo' Params = @{ FilePath = $FilePath } } $Response = Invoke-DuoRequest @DuoRequest if ($Response.stat -eq 'OK') { $Response.response } else { $Response } } } #EndRegion './Public/Auth API/Get-DuoAuthLogo.ps1' 44 #Region './Public/Auth API/Get-DuoAuthStatus.ps1' 0 function Get-DuoAuthStatus { <# .SYNOPSIS Duo API Auth Status .DESCRIPTION The /auth_status endpoint "long-polls" for the next status update from the authentication process for a given transaction. That is to say, if no status update is available at the time the request is sent, it will wait until there is an update before returning a response. .PARAMETER TxId The transaction ID of the authentication attempt, as returned by the /auth endpoint. .EXAMPLE Get-DuoAuthStatus -TxId 66cc8d20-fdfa-41bc-8b74-1a3b095d55f7 .LINK https://duo.com/docs/authapi#/auth_status .NOTES #> [CmdletBinding()] Param( [Parameter(ValueFromPipelineByPropertyName = $true, Mandatory = $true)] [string]$TxId ) process { $Params = @{ txid = $TxId } $DuoRequest = @{ Method = 'GET' Path = '/auth/v2/auth_status' Params = $Params } $Response = Invoke-DuoRequest @DuoRequest if ($Response.stat -eq 'OK') { $Response.response } else { $Response } } } #EndRegion './Public/Auth API/Get-DuoAuthStatus.ps1' 46 #Region './Public/Auth API/New-DuoAuthEnrollment.ps1' 0 function New-DuoAuthEnrollment { <# .SYNOPSIS Duo Auth Enrollment .DESCRIPTION The /enroll endpoint provides a programmatic way to enroll new users with Duo two-factor authentication. It creates the user in Duo and returns a code (as a QR code) that Duo Mobile can scan with its built-in camera. Scanning the QR code adds the user's account to the app so that they receive and respond to Duo Push login requests. .PARAMETER Username Username for the created user. If not given, a random username will be assigned and returned. .PARAMETER ValidSecs Seconds for which the activation code will remain valid. Default: 86400 (one day). .EXAMPLE New-DuoAuthEnrollment .NOTES General notes #> [CmdletBinding(SupportsShouldProcess)] Param( [Parameter()] [string]$Username, [Parameter()] [int]$ValidSecs ) process { $Params = @{} if ($Username) { $Params.username = $Username } if ($ValidSecs) { $Params.valid_secs = $ValidSecs } $DuoRequest = @{ Method = 'POST' Path = '/auth/v2/enroll' Params = $Params } if ($PSCmdlet.ShouldProcess()) { $Response = Invoke-DuoRequest @DuoRequest if ($Response.stat -eq 'OK') { $Response.response } else { $Response } } } } #EndRegion './Public/Auth API/New-DuoAuthEnrollment.ps1' 51 #Region './Public/Auth API/Send-DuoAuth.ps1' 0 function Send-DuoAuth { <# .SYNOPSIS Duo Auth .DESCRIPTION The /auth endpoint performs second-factor authentication for a user by sending a push notification to the user's smartphone app, verifying a passcode, or placing a phone call. It is also used to send the user a new batch of passcodes via SMS. .PARAMETER UserId Permanent, unique identifier for the user as generated by Duo upon user creation (e.g. DUYHV6TJBC3O4RITS1WC). .PARAMETER Username Unique identifier for the user that is commonly specified by your application during user creation (e.g. user@domain.com). This value may also represent a username alias assigned to a user. .PARAMETER Factor Factor to use for authentication. Currently, the following choices are supported: | Value | Meaning | |-------|---------| | auto | Use the out-of-band factor (push or phone) recommended by Duo as the best for the user's devices. | push | Authenticate the user with Duo Push. | passcode | Authenticate the user with a passcode (from Duo Mobile, SMS, hardware token, or bypass code). | sms | Send a new batch of SMS passcodes to the user. Note that this will not actually authenticate the user (it will automatically return "deny" Thus, if the user elects to do this then you should re-prompt to authenticate after the call has completed. | phone | Authenticate the user with phone callback. .PARAMETER IpAddr The IP address of the user to be authenticated, in dotted quad format. This will cause an "allow" response to be sent if appropriate for requests from a trusted network. .PARAMETER Hostname The host name of the device accessing the application. .PARAMETER Async If this parameter is not provided, then the /auth endpoint will only return a response when the authentication process has completed. If, however, your application provides this parameter with a value of "1", then /auth will immediately return a transaction ID, and your application will need to subsequently query the /auth_status endpoint to get the status (and, eventually, result) of the authentication process. If you enable async, then your application will be able to retrieve real-time status updates from the authentication process, rather than receiving no information until the process is complete. .PARAMETER Device ID of the device. This device must have the "push","phone" or "sms" capability. Default: auto .PARAMETER Type This string is displayed in the Duo Mobile app push notification and UI. You may wish to specify some alternate phrase for this parameter. The default English string in Duo Mobile v4 is "Verify your identity" and "Are you logging in to" followed by the application's name in the push request notification text, and "Are you logging in to" followed by the application's name in the request details screen as shown in Duo Mobile. With type specified, the notification text changes to "Verify request" and shows your customized string followed by a colon and the application's name, and the request details screen also shows your customized string and the application's name. Duo Mobile shows the equivalent localization in the languagues supported by the app, but does not attempt to localize your custom string or support multiple string values (for different languages). .PARAMETER DisplayUsername String to display in Duo Mobile in place of the user's Duo username. .PARAMETER Passcode Passcode entered by the user. .PARAMETER PushInfo A set of URL-encoded key/value pairs with additional contextual information associated with this authentication attempt. The Duo Mobile app will display this information to the user. For example: from=login%20portal&domain=example.com The URL-encoded string's total length must be less than 20,000 bytes. .EXAMPLE New-DuoAuth -Username blumbergh -Factor Auto -Async .LINK https://duo.com/docs/authapi#/auth .NOTES Exactly one of user_id or username must be specified. The push_info URL-encoded string's total length must be less than 20,000 bytes. #> [CmdletBinding(DefaultParameterSetName = 'Username')] Param( [Parameter(Mandatory = $true, ParameterSetName = 'UserId')] [Alias('user_id')] [string]$UserId, [Parameter(Mandatory = $true, ParameterSetName = 'Username')] [string]$Username, [Parameter()] [ValidateSet('Auto', 'Push', 'Passcode', 'Sms', 'Phone')] [string]$Factor = 'Auto', [Parameter()] [string]$IpAddr, [Parameter()] [string]$Hostname, [Parameter()] [switch]$Async, [Parameter()] [string]$Device = 'auto', [Parameter()] [string]$Type, [Parameter()] [string]$DisplayUsername, [Parameter()] [switch]$Passcode, [Parameter()] [hashtable]$PushInfo ) process { $Params = [ordered]@{ factor = $Factor.ToLower() } if ($UserId) { $Params.user_id = $UserId } if ($Username) { $Params.username = $Username } if ($IpAddr) { $Params.ipaddr = $IpAddr } if ($Hostname) { $Params.hostname = $Hostname } if ($Async.IsPresent) { $Params.async = 1 } if ($Factor -eq 'Passcode') { if ($Passcode) { $Params.passcode = $Passcode } } else { if ($Device) { $Params.device = $Device } if ($Factor -eq 'Push') { if ($Type) { $Params.type = $Type } if ($DisplayUsername) { $Params.display_username = $DisplayUsername } } } if ($PushInfo) { $PushInfoCollection = [System.Web.HttpUtility]::ParseQueryString([String]::Empty) foreach ($Item in $PushInfo.GetEnumerator()) { $PushInfoCollection.Add($Item.Key, $Item.Value) } $Params.pushinfo = [System.Web.HttpUtility]::UrlDecode($PushInfoCollection.ToString()) } $DuoRequest = @{ Method = 'POST' Path = '/auth/v2/auth' Params = $Params } $Response = Invoke-DuoRequest @DuoRequest if ($Response.stat -eq 'OK') { $Response.response } else { $Response } } } #EndRegion './Public/Auth API/Send-DuoAuth.ps1' 153 #Region './Public/Auth API/Send-DuoAuthPing.ps1' 0 function Send-DuoAuthPing { <# .SYNOPSIS Duo API Ping .DESCRIPTION The /ping endpoint acts as a "liveness check" that can be called to verify that Duo is up before trying to call other Auth API endpoints. Unlike the other endpoints, this one does not have to be signed with the Authorization header. .EXAMPLE Send-DuoAuthPing .LINK https://duo.com/docs/authapi#/ping .NOTES This endpoint is also suitable for use with Duo's v2 Web SDK to verify that Duo's service is responding before initializing frame authentication. #> [CmdletBinding()] Param() process { $DuoRequest = @{ Method = 'GET' Path = '/auth/v2/ping' NoAuth = $true } $Response = Invoke-DuoRequest @DuoRequest if ($Response.stat -eq 'OK') { $Response.response } else { $Response } } } #EndRegion './Public/Auth API/Send-DuoAuthPing.ps1' 36 #Region './Public/Auth API/Send-DuoPreAuth.ps1' 0 function Send-DuoPreAuth { <# .SYNOPSIS Duo Pre-Auth .DESCRIPTION The /preauth endpoint determines whether a user is authorized to log in, and (if so) returns the user's available authentication factors. .PARAMETER UserId Permanent, unique identifier for the user as generated by Duo upon user creation (e.g. DUYHV6TJBC3O4RITS1WC). .PARAMETER Username Unique identifier for the user that is commonly specified by your application during user creation (e.g. user@domain.com). This value may also represent a username alias assigned to a user .PARAMETER IpAddr The IP address of the user to be authenticated, in dotted quad format. This will cause an "allow" response to be sent if appropriate for requests from a trusted network. .PARAMETER Hostname The host name of the device accessing the application. .PARAMETER TrustedDeviceToken If the trusted_device_token is present and the Auth API application has an effective policy that enables Remembered Devices for each browser-based application, return an "allow" response for the lifetime of the token as set by the Duo administrator in the policy. .EXAMPLE Send-DuoPreAuth -Username pgibbons .LINK https://duo.com/docs/authapi#/preauth .NOTES Exactly one of user_id or username must be specified. #> [CmdletBinding(DefaultParameterSetName = 'Username')] Param( [Parameter(Mandatory = $true, ParameterSetName = 'UserId')] [Alias('user_id')] [string]$UserId, [Parameter(Mandatory = $true, ParameterSetName = 'Username')] [string]$Username, [Parameter()] [string]$IpAddr, [Parameter()] [string]$Hostname, [Parameter()] [string]$TrustedDeviceToken ) process { $Params = @{} if ($UserId) { $Params.user_id = $UserId } if ($Username) { $Params.username = $Username } if ($IpAddr) { $Params.ipaddr = $IpAddr } if ($Hostname) { $Params.hostname = $Hostname } if ($TrustedDeviceToken) { $Params.trusted_device_token = $TrustedDeviceToken } $DuoRequest = @{ Method = 'POST' Path = '/auth/v2/preauth' Params = $Params } $Response = Invoke-DuoRequest @DuoRequest if ($Response.stat -eq 'OK') { $Response.response } else { $Response } } } #EndRegion './Public/Auth API/Send-DuoPreAuth.ps1' 74 #Region './Public/Auth API/Test-DuoAuthApi.ps1' 0 function Test-DuoAuthApi { <# .SYNOPSIS Check Duo API .DESCRIPTION The /check endpoint can be called to verify that the Auth API integration and secret keys are valid, and that the signature is being generated properly. .EXAMPLE Test-DuoAuthApi .LINK https://duo.com/docs/authapi#/check .NOTES This endpoint is also suitable for use with Duo's v2 Web SDK to verify integration information before initializing frame authentication. #> [CmdletBinding()] Param() process { $DuoRequest = @{ Method = 'GET' Path = '/auth/v2/check' } $Response = Invoke-DuoRequest @DuoRequest if ($Response.stat -eq 'OK') { $Response.response } else { $Response } } } #EndRegion './Public/Auth API/Test-DuoAuthApi.ps1' 35 #Region './Public/Misc/Get-DuoAccountID.ps1' 0 function Get-DuoAccountID { <# .SYNOPSIS Get Account ID # from Duo API hostname .DESCRIPTION Converts hexidecimal hostname to decimal format .PARAMETER ApiHost API hostname to get Account ID # for .EXAMPLE Get-DuoAccountID -ApiHost api-01ab23cd.duosecurity.com #> [CmdletBinding()] Param( [Parameter(ValueFromPipelineByPropertyName = $true)] [Alias('api_hostname')] [string]$ApiHost ) $AccountHex = '' if (!$ApiHost) { if ($script:DuoApiHost) { $ApiHost = $script:DuoApiHost } elseif ($script:DuoAccountsApiHost) { $ApiHost = $script:DuoAccountsApiHost } } if (!$ApiHost) { $ApiHost = Read-Host 'Please enter an API hostname (e.g. api-01ab23cd.duosecurity.com)' } if ($ApiHost -match 'api-(?<AccountHex>.+)\.duosecurity\.com') { $AccountHex = '0x{0}' -f $Matches.AccountHex $AccountHexUnsigned = [uint]$AccountHex ($AccountHexUnsigned.ToString().PadLeft(10, '0') -split '([0-9]{4})' -ne '') -join '-' } else { Write-Output 'Unable to determine account ID' } } #EndRegion './Public/Misc/Get-DuoAccountID.ps1' 43 #Region './Public/Misc/New-DuoTokenTotpSecret.ps1' 0 function New-DuoTokenTotpSecret { <# .SYNOPSIS Creates TOTP secret in Duo format .DESCRIPTION Creates both Base32 and Hex formatted secret keys for Duo token and TOTP app .PARAMETER SecretLength Length of secret .EXAMPLE New-DuoTokenTotpSecret Base32 Hex ------ --- EMFJSRYQRRWYXX6ME5T3DYZH 230a9947108c6d8bdfcc2767b1e327 #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '')] [CmdletBinding()] Param( [Parameter()] [int]$SecretLength = 15 ) # Base32 character set $Base32Charset = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567' # Generate random byte string for secret $SecretBytes = [byte[]]::new($SecretLength) [Security.Cryptography.RNGCryptoServiceProvider]::new().GetBytes($SecretBytes, 0, $SecretLength) # Convert byte array to hexidecimal $Hex = ($SecretBytes | ForEach-Object ToString X2) -join '' # Convert byte array to binary $SecretBytesBinary = -join $SecretBytes.ForEach{ [Convert]::ToString($_, 2).PadLeft(8, '0') } # Convert binary bytes to base32 $Base32Secret = [regex]::Replace($SecretBytesBinary, '.{5}', { param($Match) $Base32Charset[[Convert]::ToInt32($Match.Value, 2)] }) [PSCustomObject]@{ Base32 = $Base32Secret Hex = $Hex.ToLower() } } #EndRegion './Public/Misc/New-DuoTokenTotpSecret.ps1' 52 #Region './Public/Apps/Authentication Proxy/Get-DuoAuthProxyLogs.ps1' 0 function Get-DuoAuthProxyLogs { <# .SYNOPSIS This script reads Duo Auth Proxy log files .DESCRIPTION Reads in log files from the designated log path and parses the entries from the standard log file format or json in the case of ssoevents .PARAMETER ListLogs List available log files .PARAMETER LogName Name of log to get .PARAMETER Search Search object for string .EXAMPLE Get-DuoAuthProxyLogs -LogName ssoevents -Search test #> [CmdletBinding()] Param( [Parameter(ParameterSetName = 'List')] [switch]$ListLogs, [Parameter(ParameterSetName = 'Logs')] [string]$LogName, [Parameter(ParameterSetName = 'Logs')] [string]$Search ) if ($IsLinux) { $ProxyBin = '/opt/duoauthproxy/bin/authproxyctl' $DuoPath = '/opt/duoauthproxy' } elseif ($IsWindows) { $ProxyBin = '{0}\Duo Security Authentication Proxy\bin\authproxyctl.exe' -f $env:ProgramFiles $DuoPath = '{0}\Duo Security Authentication Proxy' -f $env:ProgramFiles } else { throw 'Unsupported OS' } if (-not (Test-Path $ProxyBin)) { $DuoPath = '{0}\Duo Security Authentication Proxy' -f ${env:ProgramFiles(x86)} if (Test-Path $DuoPath) { Write-Warning 'You are not running a v5.1.0 or higher version of Duo Security Authentication Proxy, please update at your earliest convenience' } else { throw 'Duo Security Authentication Proxy not detected' } } $LogPath = Join-Path $DuoPath 'log' if ($ListLogs.IsPresent) { Get-ChildItem -Path $LogPath/*.log | Select-Object -ExpandProperty BaseName } else { $Logs = Get-Content -Path $LogPath/$LogName.log switch ($LogName) { 'ssoevents' { $ParsedLogs = foreach ($Log in $Logs) { $Log | ConvertFrom-Json } } 'install' { Get-Content -Path $LogPath/$LogName.log -Encoding Unicode } default { $ParsedLogs = foreach ($Log in $Logs) { if ($Log -match '^(?<Date>.+?)\s\[(?<Type>.+?)\]\s(?<Message>.+)$') { [PSCustomObject]@{ Date = Get-Date $Matches.Date Type = $Matches.Type Message = $Matches.Message } } } } } if ($Search) { $ParsedLogs -match $Search } else { $ParsedLogs } } } #EndRegion './Public/Apps/Authentication Proxy/Get-DuoAuthProxyLogs.ps1' 98 #Region './Public/Apps/Install & Upgrade/Get-DuoInstallFileInfo.ps1' 0 function Get-DuoInstallFileInfo { [CmdletBinding()] Param( $Url ) $Head = Invoke-WebRequest $Url -Method Head $ContentDisposition = @{} $Head.headers.'Content-Disposition' -split '\s*;\s*' | ForEach-Object { $Key, $Value = $_ -split '='; $ContentDisposition.$Key = $Value -replace '"' } $Filename = $ContentDisposition.filename (Invoke-WebRequest 'https://duo.com/docs/checksums').content -split "`n" | Where-Object { $_ -match "(:?<a href=`"(?<Url>.+)`">)?(?<Checksum>[a-z0-9]+)\s+?$Filename" } if ($Matches) { $Checksum = $Matches.Checksum $Url = $Matches.Url } else { $Checksum = $false $Url = $false } [PSCustomObject]@{ Name = $Filename Checksum = $Checksum Url = $Url } } #EndRegion './Public/Apps/Install & Upgrade/Get-DuoInstallFileInfo.ps1' 28 #Region './Public/Apps/Install & Upgrade/Install-DuoAuthProxy.ps1' 0 function Install-DuoApplication { [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')] Param( [Parameter(Mandatory = $true)] [ValidateSet('AuthProxy', '')] [string]$Application ) if ($PSCmdlet.ShouldProcess($Application)) { } } #EndRegion './Public/Apps/Install & Upgrade/Install-DuoAuthProxy.ps1' 13 #Region './Public/Authentication/Set-DuoApiAuth.ps1' 0 function Set-DuoApiAuth { <# .SYNOPSIS Sets credentials for Duo .DESCRIPTION Saves credentials as script scoped variables for use in the module .PARAMETER ApiHost Hostname (excluding https://) .PARAMETER IntegrationKey Integration key .PARAMETER SecretKey Secret key .PARAMETER Type Type of credential #> [CmdletBinding(SupportsShouldProcess)] Param( [Parameter(ValueFromPipelineByPropertyName = $true, Mandatory = $true)] [Alias('api_hostname')] [string]$ApiHost, [Parameter(ValueFromPipelineByPropertyName = $true, Mandatory = $true)] [Alias('integration_key')] [string]$IntegrationKey, [Parameter(ValueFromPipelineByPropertyName = $true, Mandatory = $true)] [Alias('secret_key')] [string]$SecretKey, [ValidateSet('Accounts', 'Admin', 'Auth')] [string]$Type = 'Admin' ) process { if ($PSCmdlet.ShouldProcess($Type)) { switch ($Type) { 'Accounts' { $script:DuoAccountsApiHost = $ApiHost $script:DuoAccountsIntegrationKey = $IntegrationKey $script:DuoAccountsSecretKey = $SecretKey } 'Admin' { $script:DuoApiHost = $ApiHost $script:DuoIntegrationKey = $IntegrationKey $script:DuoSecretKey = $SecretKey } 'Auth' { $script:DuoAuthApiHost = $ApiHost $script:DuoAuthIntegrationKey = $IntegrationKey $script:DuoAuthSecretKey = $SecretKey } } } } } #EndRegion './Public/Authentication/Set-DuoApiAuth.ps1' 60 #Region './Public/Accounts API/Get-DuoAccountEdition.ps1' 0 function Get-DuoAccountEdition { <# .SYNOPSIS Get Edition .DESCRIPTION Returns the edition for a child account. .PARAMETER AccountId The child customer account ID as returned by Retrieve Accounts. This is a 20 character string, for example DA9VZOC5X63I2W72NRP9. .EXAMPLE Get-DuoAccounts | Select-Object name,account_id, @{n='edition'; e={($_ | Get-DuoAccountEdition).edition}} .INPUTS PSCustomObject. Duo Accounts object .OUTPUTS PSCustomObject. Returns a Duo Response object. .LINK https://duo.com/docs/accountsapi#get-edition #> [CmdletBinding(SupportsShouldProcess)] Param( [Parameter(ValueFromPipelineByPropertyName = $true, Mandatory = $true)] [Alias('account_id')] [string]$AccountId ) process { Select-DuoAccount -AccountId $AccountId -Quiet $DuoRequest = @{ Method = 'GET' Path = '/admin/v1/billing/edition' } if ($PSCmdlet.ShouldProcess($AccountId)) { $Response = Invoke-DuoRequest @DuoRequest if ($Response.stat -eq 'OK') { $Response.response } else { $Response } } } } #EndRegion './Public/Accounts API/Get-DuoAccountEdition.ps1' 50 #Region './Public/Accounts API/Get-DuoAccounts.ps1' 0 function Get-DuoAccounts { <# .SYNOPSIS Retrieve Accounts .DESCRIPTION Returns a list of child accounts. .EXAMPLE Get-DuoAccounts .INPUTS None .OUTPUTS PSCustomObject. Returns a Duo Response object. .LINK https://duo.com/docs/accountsapi#retrieve-accounts #> [CmdletBinding()] Param( [switch]$IncludeEdition ) $DuoRequest = @{ Method = 'POST' Path = '/accounts/v1/account/list' } $Response = Invoke-DuoRequest @DuoRequest if ($Response.stat -eq 'OK') { if ($IncludeEdition.IsPresent) { $Accounts = $Response.response | Select-Object *, @{n = 'account_id_num'; e = { $_ | Get-DuoAccountID } }, @{ n = 'edition'; e = { ($_ | Get-DuoAccountEdition).edition } } } else { $Accounts = $Response.response | Select-Object *, @{n = 'account_id_num'; e = { $_ | Get-DuoAccountID } } } $script:DuoAccountsList = $Accounts $Accounts } else { $Response } } #EndRegion './Public/Accounts API/Get-DuoAccounts.ps1' 47 #Region './Public/Accounts API/Get-DuoAccountTelephonyCredits.ps1' 0 function Get-DuoAccountTelephonyCredits { <# .SYNOPSIS Get Telephony Credits .DESCRIPTION Returns the available telephony credits for a child account. .PARAMETER AccountId The child customer account ID as returned by Retrieve Accounts. This is a 20 character string, for example DA9VZOC5X63I2W72NRP9. .EXAMPLE Get-DuoAccounts | Select-Object name,account_id, @{n='credits'; e={($_ | Get-DuoAccountTelephonyCredits).credits}} .INPUTS PSCustomObject. Duo Accounts object .OUTPUTS PSCustomObject. Returns a Duo Response object. .LINK https://duo.com/docs/accountsapi#get-telephony-credits #> [CmdletBinding()] Param( [Parameter(ValueFromPipelineByPropertyName = $true, Mandatory = $true)] [Alias('account_id')] [string]$AccountId ) process { Select-DuoAccount -AccountId $AccountId -Quiet $DuoRequest = @{ Method = 'GET' Path = '/admin/v1/billing/telephony_credits' } $Response = Invoke-DuoRequest @DuoRequest if ($Response.stat -eq 'OK') { $Response.response } else { $Response } } } #EndRegion './Public/Accounts API/Get-DuoAccountTelephonyCredits.ps1' 48 #Region './Public/Accounts API/New-DuoAccount.ps1' 0 function New-DuoAccount { <# .SYNOPSIS Create Account .DESCRIPTION Create a new child account. .PARAMETER Name Name for the new customer. .EXAMPLE New-DuoAccount -Name 'Some Company' .INPUTS None .OUTPUTS PSCustomObject. Returns a Duo Response object. .LINK https://duo.com/docs/accountsapi#create-account #> [CmdletBinding(SupportsShouldProcess)] Param( [Parameter(Mandatory = $true)] [string]$Name ) $DuoRequest = @{ Method = 'POST' Path = '/accounts/v1/account/create' Params = @{ name = $Name } } if ($PSCmdlet.ShouldProcess($Name)) { Invoke-DuoRequest @DuoRequest } } #EndRegion './Public/Accounts API/New-DuoAccount.ps1' 42 #Region './Public/Accounts API/Remove-DuoAccount.ps1' 0 function Remove-DuoAccount { <# .SYNOPSIS Delete Account .DESCRIPTION Delete the account with ID account_id from the system. .PARAMETER AccountId ID of the customer account to delete as returned by Retrieve Accounts. This is a 20 character string, for example DA9VZOC5X63I2W72NRP9. .EXAMPLE Remove-DuoAccount -AccountId SOMEACCOUNTID .INPUTS PSCustomObject. Duo Accounts object .OUTPUTS PSCustomObject. Returns a Duo Response object. .LINK https://duo.com/docs/accountsapi#delete-account #> [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')] Param( [Parameter(ValueFromPipelineByPropertyName = $true, Mandatory = $true)] [Alias('account_id')] [string]$AccountId ) process { $DuoRequest = @{ Method = 'POST' Path = '/accounts/v1/account/delete' Params = @{ account_id = $AccountId } } if ($PSCmdlet.ShouldProcess($AccountId)) { Invoke-DuoRequest @DuoRequest } } } #EndRegion './Public/Accounts API/Remove-DuoAccount.ps1' 45 #Region './Public/Accounts API/Select-DuoAccount.ps1' 0 function Select-DuoAccount { <# .SYNOPSIS Select Duo Account to use for Admin API .DESCRIPTION Takes values from the account list and creates API credentials for sub account .PARAMETER AccountId Duo Account Id .PARAMETER Name Duo Account name .PARAMETER Clear Clear credentials .PARAMETER Quiet Suppress output .EXAMPLE Select-DuoAccount -Name 'Some Company Name' .EXAMPLE Select-DuoAccount -AccountId SOMEACCOUNTID #> [CmdletBinding()] Param( [Parameter(Mandatory = $true, ParameterSetName = 'AccountId')] [string]$AccountId, [Parameter(Mandatory = $true, ParameterSetName = 'AccountName')] [string]$Name, [Parameter(ParameterSetName = 'Clear')] [switch]$Clear, [Parameter()] [switch]$Quiet ) if ($Clear) { $script:DuoApiHost = $script:DuoAccountsApiHost $script:DuoAccountId = $null } if (!$script:DuoAccountsList) { Get-DuoAccounts | Out-Null } if ($Name) { $Account = $script:DuoAccountsList | Where-Object { $_.name -eq $Name } } if ($AccountId) { $Account = $script:DuoAccountsList | Where-Object { $_.account_id -eq $AccountId } } if ($Account) { $script:DuoApiHost = $Account.api_hostname $script:DuoIntegrationKey = $script:DuoAccountsIntegrationKey $script:DuoSecretKey = $script:DuoAccountsSecretKey $script:DuoAccountId = $Account.account_id if (!$Quiet) { Write-Information "Account: $($Account.name) ($($Account.account_id))" } } else { Write-Error 'Invalid Account specified' } } #EndRegion './Public/Accounts API/Select-DuoAccount.ps1' 71 #Region './Public/Accounts API/Set-DuoAccountEdition.ps1' 0 function Set-DuoAccountEdition { <# .SYNOPSIS Set Edition .DESCRIPTION Sets the edition for a child account. .PARAMETER AccountId The child customer account ID as returned by Retrieve Accounts. This is a 20 character string, for example DA9VZOC5X63I2W72NRP9. .PARAMETER Edition The edition to set. This should be one of: ENTERPRISE PLATFORM BEYOND .EXAMPLE Set-DuoAccountEdition -AccountId SOMEACCOUNTID -Edition 'BEYOND' .LINK https://duo.com/docs/accountsapi#set-edition #> [CmdletBinding(SupportsShouldProcess)] Param( [Parameter(ValueFromPipelineByPropertyName = $true, Mandatory = $true)] [Alias('account_id')] [string]$AccountId, [Parameter(ValueFromPipelineByPropertyName = $true, Mandatory = $true)] [ValidateSet('ENTERPRISE', 'PLATFORM', 'BEYOND')] [string]$Edition ) process { Select-DuoAccount -AccountId $AccountId -Quiet $DuoRequest = @{ Method = 'GET' Path = '/admin/v1/billing/edition' Params = @{ edition = $Edition } } if ($PSCmdlet.ShouldProcess($AccountId)) { $Response = Invoke-DuoRequest @DuoRequest if ($Response.stat -eq 'OK') { $Response.response } else { $Response } } } } #EndRegion './Public/Accounts API/Set-DuoAccountEdition.ps1' 55 #Region './Public/Accounts API/Set-DuoAccountTelephonyCredits.ps1' 0 function Set-DuoAccountTelephonyCredits { <# .SYNOPSIS Set Telephony Credits .DESCRIPTION Sets the telephony credits for a child account. .PARAMETER AccountId The child customer account ID as returned by Retrieve Accounts. This is a 20 character string, for example DA9VZOC5X63I2W72NRP9. .PARAMETER Edition The total number of credits that the child account will have after transferring credits from the parent account. .EXAMPLE Set-DuoAccountTelephonyCredits -AccountId SOMEACCOUNTID -Credits .LINK https://duo.com/docs/accountsapi#set-telephony-credits .NOTES Any additional credits added to the child account are transferred from the parent account. For example, if the child account has 100 credits and it is then set to 300 credits, then 200 credits are deducted from the parent's balance and added to the child's balance. #> [CmdletBinding(SupportsShouldProcess)] Param( [Parameter(ValueFromPipelineByPropertyName = $true, Mandatory = $true)] [Alias('account_id')] [string]$AccountId, [Parameter(ValueFromPipelineByPropertyName = $true, Mandatory = $true)] [int]$Credits ) process { Select-DuoAccount -AccountId $AccountId -Quiet $DuoRequest = @{ Method = 'GET' Path = '/admin/v1/billing/edition' Parameters = @{ credits = $Credits } } if ($PSCmdlet.ShouldProcess($AccountId)) { $Response = Invoke-DuoRequest @DuoRequest if ($Response.stat -eq 'OK') { $Response.response } else { $Response } } } } #EndRegion './Public/Accounts API/Set-DuoAccountTelephonyCredits.ps1' 54 |