Examples/DWAPIExamples.psm1


Function Get-AzAccessToken{
    [OutputType([String])]
    Param (
        [parameter(Mandatory=$True)]
        [string]$TenantId,

        [parameter(Mandatory=$True)]
        [string]$ClientId,

        [Parameter(Mandatory=$True)]
        [string]$ClientSecret,

        [Parameter(Mandatory=$false)]
        [string]$Scope
    )

    $OAuthURI = "https://login.microsoftonline.com/$TenantId/oauth2/token"

    $OAuthBody=@{}
    $OAuthBody.Add('grant_type','client_credentials')
    $OAuthBody.Add('client_id',$ClientId)
    $OAuthBody.Add('client_secret',$ClientSecret)
    if ($scope)
    {
        $OAuthBody.Add('scope',$scope)
    }
    else
    {
        $OAuthBody.Add('resource','https://graph.microsoft.com')
    }

    $OAuthheaders =
    @{
        "content-type" = "application/x-www-form-urlencoded"
    }

    $accessToken = Invoke-RESTMethod -Method 'POST' -URI $OAuthURI -Body $OAuthBody -Headers $OAuthheaders

    return $accessToken.access_Token
    <#
    .Synopsis
    Gets a session bearer token for the Azure credentials provided.

    .Description
    Takes the three required Azure credentials and returns the OAuth2 access token provided by the Microsoft Graph authentication provider.

    .Parameter TenantId
    The Directory or tenant ID of the Azure system being connected to.

    .Parameter ClientId
    The Client Id or Application ID connecting.

    .Parameter ClientSecret
    The client secret of the client / application being used to connect.

    .Outputs
    Output type [string]
    The text string containing the OAuth2 accessToken returned from the Azure authentication provider.

    .Example
    # Get the AccessToken for the credentials passed.
    $accessToken = Get-AzAccessToken -TenantId $TenantId -ClientId $ClientId -ClientSecret $ClientSecret
    #>

}
Export-Modulemember -function Get-AzAccessToken

Function Get-AzureUserTable([string]$accessToken){

    <#
    .Synopsis
    Get a datatable containing all Azure user data.

    .Description
    Uses the /users Graph endpoint to pull all user information into a data table for insersion into a SQL database.

    .Parameter accessToken
    The access token for the session you are pulling information from.

    .Outputs
    Output type [system.data.datatable]
    A datatable containing all of the rows returned by the /users Graph API Endpoint.

    .Notes
    Any nested data returned by Azure will be pushed into the data table as a string containing the nested JSON.

    .Example
    # Get the user data for the access token passed.
    $dtAzureUserData = Get-AzureUsers -accessToken $AccessToken
    #>


    [OutputType([PSObject])]

    $uri='https://graph.microsoft.com/v1.0/users?$select=id,accountEnabled,ageGroup,assignedLicenses,assignedPlans,businessPhones,city,companyName,consentProvidedForMinor,country,createdDateTime,creationType,deletedDateTime,department,displayName,employeeHireDate,employeeId,employeeOrgData,employeeType,externalUserState,externalUserStateChangeDateTime,faxNumber,givenName,id,identities,imAddresses,isResourceAccount,jobTitle,lastPasswordChangeDateTime,legalAgeGroupClassification,licenseAssignmentStates,mail,mailNickname,mobilePhone,officeLocation,onPremisesDistinguishedName,onPremisesDomainName,onPremisesExtensionAttributes,onPremisesImmutableId,onPremisesLastSyncDateTime,onPremisesProvisioningErrors,onPremisesSamAccountName,onPremisesSecurityIdentifier,onPremisesSyncEnabled,onPremisesUserPrincipalName,otherMails,passwordPolicies,passwordProfile,postalCode,preferredDataLocation,preferredLanguage,provisionedPlans,proxyAddresses,refreshTokensValidFromDateTime,showInAddressList,signInSessionsValidFromDateTime,state,streetAddress,surname,usageLocation,userPrincipalName,userType'

    $FirstRun = $True

    Do
    {
        $users = Invoke-RestMethod -Headers @{Authorization = "Bearer $($accessToken)" } -Uri $uri -Method Get

        if($FirstRun)
        {
            $dtResults = New-Object System.Data.DataTable
            $ScriptBlock=$null

            foreach($object_properties in $($users.Value[0] | Get-Member | where-object{$_.MemberType -eq "NoteProperty"}))
            {

                $DataType = switch ($object_properties.Definition.substring(0,$object_properties.Definition.IndexOf(' ')))
                {
                    'datetime' {'datetime'}
                    'bool' {'boolean'}
                    'long' {'int64'}
                    'string' {'string'}
                    'object' {'string'}
                    default {'string'}
                }
                $dtResults.Columns.Add($object_properties.Name,$datatype) | Out-Null

                $ScriptBlock += 'if ($entry.' + $object_properties.Name + ' -ne $null) {if ($entry.' + $object_properties.Name + '.Value -ne $null) { $DataRow.' + $object_properties.Name + ' = $entry.' + $object_properties.Name + '.Value }else{ if ($entry.' + $object_properties.Name + '.GetType().Name -eq "Object[]") { $DataRow.' + $object_properties.Name + ' = ($entry.' + $object_properties.Name + ' | ConvertTo-JSON).ToString() } else { $DataRow.' + $object_properties.Name + ' = $entry.' + $object_properties.Name + ' } } } else {$DataRow.' + $object_properties.Name + " = [DBNULL]::Value}`n"
            }
        }

        $FirstRun = $False #After the first iteration, don't try to add the data columns

        $ScriptBlock = $ExecutionContext.InvokeCommand.NewScriptBlock($ScriptBlock)

        foreach($entry in $users.Value)
        {
            $DataRow = $dtResults.NewRow()

            & $ScriptBlock

            $dtResults.Rows.Add($DataRow)
        }

        $uri = $users.'@odata.nextLink'
    }
    while ($null -ne $users.'@odata.nextLink')

    return @(,($dtResults))

}
Export-Modulemember -function Get-AzureUserTable

Function Get-IntuneDeviceTable([string]$accessToken){

    <#
    .Synopsis
    Get a datatable containing all InTune device data.

    .Description
    Uses the /deviceManagement/managedDevices Graph endpoint to pull all device information into a data table for insersion into a SQL database.

    .Parameter accessToken
    The access token for the session you are pulling information from.

    .Outputs
    Output type [system.data.datatable]
    A datatable containing all of the rows returned by the /deviceManagement/managedDevices Graph API Endpoint.

    .Notes
    Any nested data returned by Azure will be pushed into the data table as a string containing the nested JSON.

    .Example
    # Get the InTune data for the access token passed.
    $dtInTuneData = Get-IntuneDevices -accessToken $AccessToken
    #>


    [OutputType([PSObject])]

    $dtResults = New-Object System.Data.DataTable

    $uri='https://graph.microsoft.com/v1.0/deviceManagement/managedDevices'

    $CreateTable = $True

    Do
    {
        $devices = Invoke-RestMethod -Headers @{Authorization = "Bearer $($accessToken)" } -Uri $uri -Method Get

        $ScriptBlock=$null

        foreach($object_properties in $($devices.Value[0] | Get-Member | where-object{$_.MemberType -eq "NoteProperty"}))
        {
            if($CreateTable)
            {
                $DataType = switch ($object_properties.Definition.substring(0,$object_properties.Definition.IndexOf(' ')))
                {
                    'datetime' {'datetime'}
                    'bool' {'boolean'}
                    'long' {'int64'}
                    'string' {'string'}
                    'object' {'string'}
                    default {'string'}
                }
                $dtResults.Columns.Add($object_properties.Name,$datatype) | Out-Null
            }

            $ScriptBlock += 'if ($entry.' + $object_properties.Name + ' -ne $null) {if ($entry.' + $object_properties.Name + '.Value -ne $null) { $DataRow.' + $object_properties.Name + ' = $entry.' + $object_properties.Name + '.Value }else{ if ($entry.' + $object_properties.Name + '.GetType().Name -eq "Object[]") { $DataRow.' + $object_properties.Name + ' = ($entry.' + $object_properties.Name + ' | ConvertTo-JSON).ToString() } else { $DataRow.' + $object_properties.Name + ' = $entry.' + $object_properties.Name + ' } } } else {$DataRow.' + $object_properties.Name + " = [DBNULL]::Value}`n"
        }

        $CreateTable = $False #After the first iteration, don't try to add the data columns

        $ScriptBlock = $ExecutionContext.InvokeCommand.NewScriptBlock($ScriptBlock)

        foreach($entry in $devices.Value)
        {
            $DataRow = $dtResults.NewRow()

            & $ScriptBlock

            $dtResults.Rows.Add($DataRow)
        }

        #$devicesDatatable = $devices.value | Out-DataTable
        #$devicesDatatable.Columns.Add("DistHierId", [int], $DistHierId) | Out-Null

        $uri = $devices.'@odata.nextLink'
    }
    while ($null -ne $users.'@odata.nextLink')

    return @(,($dtResults))
}
Export-Modulemember -function Get-IntuneDeviceTable

Function Convert-DwAPIDeviceFromInTune($IntuneDataTable){
    [OutputType([System.Data.DataTable])]
    $dataTable = New-Object System.Data.DataTable

    $dataTable.Columns.Add("uniqueIdentifier", [string]) | Out-Null
    $dataTable.Columns.Add("hostname", [string]) | Out-Null
    $dataTable.Columns.Add("operatingSystemName", [string]) | Out-Null
    $dataTable.Columns.Add("operatingSystemVersion", [string]) | Out-Null
    $dataTable.Columns.Add("computerManufacturer", [string]) | Out-Null
    $dataTable.Columns.Add("computerModel", [string]) | Out-Null
    $dataTable.Columns.Add("firstSeenDate", [datetime]) | Out-Null
    $dataTable.Columns.Add("lastSeenDate", [datetime]) | Out-Null
    $dataTable.Columns.Add("serialNumber", [string]) | Out-Null
    $dataTable.Columns.Add("memoryKb", [string]) | Out-Null
    $dataTable.Columns.Add("macAddress", [string]) | Out-Null
    $dataTable.Columns.Add("totalHDDSpaceMb", [string]) | Out-Null
    $dataTable.Columns.Add("targetDriveFreeSpaceMb", [string]) | Out-Null


    foreach($Row in $dtInTuneData.Rows)
    {
        $NewRow = $null
        $NewRow = $dataTable.NewRow()

        $NewRow.uniqueComputerIdentifier = $Row.id
        $NewRow.hostname = $Row.deviceName
        $NewRow.operatingSystemName = $Row.operatingSystem
        $NewRow.operatingSystemVersion = $Row.osVersion
        $NewRow.computerManufacturer = $Row.manufacturer
        $NewRow.computerModel = $Row.model
        if ($Row.enrolledDateTime -gt '1753-01-01'){$NewRow.firstSeenDate = $Row.enrolledDateTime}
        if ($Row.lastSyncDateTime -gt '1753-01-01'){$NewRow.lastSeenDate = $Row.lastSyncDateTime}
        $NewRow.serialNumber = $Row.serialNumber
        $NewRow.memoryKb = if($Row.physicalMemoryInBytes){($Row.physicalMemoryInBytes)/1024}else{[DBNULL]::Value}
        $NewRow.macAddress = If($Row.ethernetMacAddress){$Row.ethernetMacAddress}elseif($Row.wiFiMacAddress){$Row.wiFiMacAddress}else{[DBNULL]::Value}
        $NewRow.totalHDDSpaceMb = If($Row.totalStorageSpaceInBytes){$Row.totalStorageSpaceInBytes/(1024*1024)}else{[DBNULL]::Value}
        $NewRow.targetDriveFreeSpaceMb = If($Row.freeStorageSpaceInBytes){$Row.freeStorageSpaceInBytes/(1024*1024)}else{[DBNULL]::Value}

        $dataTable.Rows.Add($NewRow)
    }

    Return ,$dataTable

    <#
    .Synopsis
    Return a datatable in the DWAPI Computers data format from the Get-IntuneDevices return table

    .Description
    Takes in a datatable returned from the Get-IntuneDevices and strips the fields required for insertion into the Dashworks Computer API.

    .Parameter IntuneDataTable
    A System.Data.DataTable object returned from the Get-IntuneDevices function in the DWAzure module

    .Outputs
    Output type [System.Data.DataTable]
    A table with the schema as used in the DW Computers API calls populated with the provided data from InTune.

    .Example
    # Convert the data for use in the DWAPI
    $dtDashworksInput = Convert-DWDeviceFromInTune -IntuneDataTable $dtInTuneData
    #>

}
Export-Modulemember -function Convert-DwAPIDeviceFromInTune

Function Convert-DwAPIUserFromAzure($AzureDataTable){
    [OutputType([System.Data.DataTable])]
    $dataTable = New-Object System.Data.DataTable

    $dataTable.Columns.Add("username", [string]) | Out-Null
    $dataTable.Columns.Add("commonObjectName", [string]) | Out-Null
    $dataTable.Columns.Add("displayName", [string]) | Out-Null
    $dataTable.Columns.Add("objectGuid", [string]) | Out-Null
    $dataTable.Columns.Add("lastLogonDate", [datetime]) | Out-Null
    $dataTable.Columns.Add("disabled", [string]) | Out-Null
    $dataTable.Columns.Add("surname", [string]) | Out-Null
    $dataTable.Columns.Add("givenName", [string]) | Out-Null
    $dataTable.Columns.Add("emailAddress", [string]) | Out-Null
    $dataTable.Columns.Add("userPrincipalName", [string]) | Out-Null


    foreach($Row in $AzureDataTable.Rows)
    {
        $NewRow = $null
        $NewRow = $dataTable.NewRow()

        $NewRow.username = $Row.userPrincipalName
        $NewRow.commonObjectName = $Row.userPrincipalName
        $NewRow.displayName = $Row.displayName
        $NewRow.objectGuid = $Row.id
        if ($Row.refreshTokensValidFromDateTime -gt '1753-01-01'){$NewRow.lastLogonDate = $Row.refreshTokensValidFromDateTime}
        $NewRow.disabled = -not $Row.accountEnabled
        $NewRow.surname = $Row.surname
        $NewRow.givenName = $Row.givenName
        $NewRow.emailAddress = $Row.userPrincipalName
        $NewRow.userPrincipalName = $Row.userPrincipalName

        $dataTable.Rows.Add($NewRow)
    }

    Return ,$dataTable

    <#
    .Synopsis
    Return a datatable in the DWAPI User data format from the Get-AzureUsers return table

    .Description
    Takes in a datatable returned from the Get-AzureUsers and strips the fields required for insertion into the Dashworks User API.

    .Parameter IntuneDataTable
    A System.Data.DataTable object returned from the Get-AzureUsers function in the DWAzure module

    .Outputs
    Output type [System.Data.DataTable]
    A table with the schema as used in the DW Users API calls populated with the provided data from Azure.

    .Example
    # Convert the data for use in the DWAPI
    $dtDashworksInput = Convert-DwAPIUserFromAzure -AzureDataTable $dtAzureUserData
    #>

}
Export-Modulemember -function Convert-DwAPIUserFromAzure

function Invoke-DwAPIUploadDeviceFeedDataTable{
    <#
    .Synopsis
    Loops a correctly formatted data table inserting all of the rows it contains.

    .Description
    Takes a System.Data.Datatable object with the columns required for the DwAPI device. Inserts these devices one at a time.

    .Parameter Instance
    The URI to the Dashworks instance being examined.

    .Parameter APIKey
    The APIKey for a user with access to the required resources.

    .Parameter FeedName
    The name of the feed to be searched for and used.

    .Parameter FeedId
    The id of the device feed to be used.

    .Parameter DWDataTable
    [System.Data.DataTable] Data table containing the fields required to insert data into the DW Device API.

    .Outputs
    Output type [string]
    Text confirming the number of rows to be inserted.

    .Example
    # Get the device feed id for the named feed.
    Write-DeviceFeedData -Instance $Instance -DWDataTable $dtDashworksInput -FeedId $DeviceImportID -APIKey $APIKey
    #>


    [OutputType([string])]
    [CmdletBinding(SupportsShouldProcess)]
    Param (
        [parameter(Mandatory=$True)]
        [string]$Instance,

        [Parameter(Mandatory=$True)]
        [System.Data.DataTable]$DWDataTable,

        [Parameter(Mandatory=$True)]
        [string]$APIKey,

        [parameter(Mandatory=$False)]
        [string[]]$FeedName = $null,

        [parameter(Mandatory=$False)]
        [string[]]$FeedId
    )

    $RowCount = 0

    if (-not $FeedId)
    {
        if (-not $FeedName)
        {
            return 'Device feed not found by name or ID'
        }

        $FeedId = Get-DwImportDeviceFeed -Instance $Instance -ApiKey $APIKey -Name $FeedName

        if (-not $FeedId)
        {
            return 'Device feed not found by name or ID'
        }
    }

    $Postheaders = @{
        "content-type" = "application/json"
        "X-API-KEY" = "$APIKey"
    }

    $uri = "{0}/apiv2/imports/devices/{1}/items" -f $Instance, $FeedId

    foreach($Row in $DWDataTable)
    {
        $Body = $null
        $Body = $Row | Select-Object * -ExcludeProperty ItemArray, Table, RowError, RowState, HasErrors | ConvertTo-Json
        try {
            if ($PSCmdlet.ShouldProcess($UniqueComputerIdentifier)) {
                Invoke-RestMethod -Headers $Postheaders -Uri $uri -Method Post -Body $Body | out-null
            }
        }
        catch {
            Write-Error ("{0}. {1}" -f $_.Exception.Response.StatusCode.Value__, ($_ | ConvertFrom-Json).details)
            break
        }
        $RowCount++
    }

    Return "$RowCount devices added"
}
Export-Modulemember -function Invoke-DwAPIUploadDeviceFeedDataTable

function Invoke-DwAPIAUploadUserFeedDataTable {

    <#
    .Synopsis
    Loops a correctly formatted data table inserting all of the rows it contains.

    .Description
    Takes a System.Data.Datatable object with the columns required for the DwAPI device. Inserts these devices one at a time.

    .Parameter Instance
    The URI to the Dashworks instance being examined.

    .Parameter APIKey
    The APIKey for a user with access to the required resources.

    .Parameter FeedName
    The name of the feed to be searched for and used.

    .Parameter FeedId
    The id of the device feed to be used.

    .Parameter DWDataTable
    [System.Data.DataTable] Data table containing the fields required to insert data into the DW Device API.

    .Outputs
    Output type [string]
    Text confirming the number of rows to be inserted.

    .Example
    # Get the device feed id for the named feed.
    Write-DeviceFeedData -Instance $Instance -DWDataTable $dtDashworksInput -FeedId $DeviceImportID -APIKey $APIKey
    #>


    [OutputType([string])]
    [CmdletBinding(SupportsShouldProcess)]
    Param (
        [parameter(Mandatory=$True)]
        [string]$Instance,

        [Parameter(Mandatory=$True)]
        [System.Data.DataTable]$DWDataTable,

        [Parameter(Mandatory=$True)]
        [string]$APIKey,

        [parameter(Mandatory=$False)]
        [string[]]$FeedName = $null,

        [parameter(Mandatory=$False)]
        [string[]]$FeedId
    )
    $RowCount = 0

    if (-not $FeedId)
    {
        if (-not $FeedName)
        {
            return 'Device feed not found by name or ID'
        }

        $FeedId = Get-DwImportDeviceFeed -Instance $Instance -ApiKey $APIKey -Name $FeedName

        if (-not $FeedId)
        {
            return 'Device feed not found by name or ID'
        }
    }

    $Postheaders = @{
        "content-type" = "application/json"
        "X-API-KEY" = "$APIKey"
    }

    $uri = "{0}/apiv2/imports/users/{1}/items" -f $Instance, $FeedId

    foreach($Row in $DWDataTable)
    {
        $Body = $null
        $Body = $Row | Select-Object * -ExcludeProperty ItemArray, Table, RowError, RowState, HasErrors | ConvertTo-Json
        try {
            if (-not $PSCmdlet.ShouldProcess($Row.Username)) {
                WRITE-OUTPUT "Got here also"
                Invoke-RestMethod -Headers $Postheaders -Uri $uri -Method Post -Body $Body | out-null
            }
        }
        catch {
            Write-Error ("{0}. {1}" -f $_.Exception.Response.StatusCode.Value__, ($_ | ConvertFrom-Json).details)
            break
        }
        $RowCount++
    }

    Return "$RowCount users added"

}
Export-Modulemember -function Invoke-DwAPIAUploadUserFeedDataTable

function Invoke-DwAPIAUploadUserFeedFromAD {
    <#
    .Synopsis
    Pulls user data from Get-ADUser and upload to a DW User feed

    .Description
    Takes all users from Get-ADUser (optional server/cred parameters) transforms the fields into a datatable in the format required
    for the DW API and then uploads that user data to a named or numbered data feed.

    .Parameter Instance
    The URI to the Dashworks instance being examined.

    .Parameter APIKey
    The APIKey for a user with access to the required resources.

    .Parameter FeedName
    The name of the feed to be searched for and used.

    .Parameter FeedId
    The id of the user feed in question.

    .Parameter ADServer
    The name of a DC to connect Get-ADUser to.

    .Parameter Credentials
    The credentials to use when calling Get-ADUser

    .Outputs
    Output type [string]
    Text confirming the number of rows to be inserted.

    .Example
    # Get the device feed id for the named feed.
    Invoke-DwAPIAUploadUserFeedFromAD -Instance $Instance -APIKey $APIKey -FeedName "AD Users"
    #>


    Param (
        [parameter(Mandatory=$True)]
        [string]$Instance,

        [Parameter(Mandatory=$True)]
        [string]$APIKey,

        [parameter(Mandatory=$False)]
        [string]$FeedName = $null,

        [parameter(Mandatory=$False)]
        [string]$FeedId,

        [parameter(Mandatory=$False)]
        [string]$ADServer,

        [parameter(Mandatory=$False)]
        [PSCredential]$Credential
    )

    $Properties = @("lastlogontimestamp","description","homeDirectory","homeDrive","mail","CanonicalName")

    if ($ADServer)
    {
        if ($Cred)
        {
            $ADUsers = get-aduser -Filter * -Properties $properties -Server $ADServer -Credential $Credential
        }
        else
        {
            $ADUsers = get-aduser -Filter * -Properties $properties -Server $ADServer
        }
    }else{
        if ($Cred)
        {
            $ADUsers = get-aduser -Filter * -Properties $properties -Credential $Credential
        }
        else
        {
            $ADUsers = get-aduser -Filter * -Properties $properties
        }
    }

    $dataTable = New-Object System.Data.DataTable

    $dataTable.Columns.Add("username", [string]) | Out-Null
    $dataTable.Columns.Add("commonObjectName", [string]) | Out-Null
    $dataTable.Columns.Add("displayName", [string]) | Out-Null
    $dataTable.Columns.Add("objectSid", [string]) | Out-Null
    $dataTable.Columns.Add("objectGuid", [string]) | Out-Null
    $dataTable.Columns.Add("lastLogonDate", [datetime]) | Out-Null
    $dataTable.Columns.Add("disabled", [string]) | Out-Null
    $dataTable.Columns.Add("surname", [string]) | Out-Null
    $dataTable.Columns.Add("givenName", [string]) | Out-Null
    $dataTable.Columns.Add("description", [string]) | Out-Null
    $dataTable.Columns.Add("homeDirectory", [string]) | Out-Null
    $dataTable.Columns.Add("homeDrive", [string]) | Out-Null
    $dataTable.Columns.Add("emailAddress", [string]) | Out-Null
    $dataTable.Columns.Add("userPrincipalName", [string]) | Out-Null
    $dataTable.Columns.Add("adCanonicalName", [string]) | Out-Null

    foreach($User in $ADUsers)
    {
        $NewRow = $null
        $NewRow = $dataTable.NewRow()

        $NewRow.username = $User.SamAccountName
        $NewRow.commonObjectName = $User.CN
        $NewRow.displayName = $User.Name
        $NewRow.objectSid = $User.sid
        $NewRow.objectGuid = $User.objectGuid
        $NewRow.lastLogonDate = if ([datetime]::FromFileTime($User.lastlogontimestamp) -gt '1753-01-01'){[datetime]::FromFileTime($User.lastlogontimestamp)}else{[DBNull]::Value}
        $NewRow.disabled = -not $User.Enabled
        $NewRow.surname = $User.surname
        $NewRow.givenName = $User.givenName
        $NewRow.description = $User.Description
        $NewRow.homeDirectory = $User.homeDirectory
        $NewRow.homeDrive = $User.homeDrive
        $NewRow.emailAddress = $User.mail
        $NewRow.userPrincipalName = $User.userPrincipalName
        $NewRow.adCanonicalName = $User.CanonicalName

        $dataTable.Rows.Add($NewRow)
    }


    $RowCount = 0

    if (-not ($FeedId))
    {
        if (-not ($FeedName))
        {
            throw 'Device feed not found by name or ID'
        }

        $FeedId = Get-DwAPIUserFeed -Instance $Instance -ApiKey $APIKey -FeedName $FeedName

        if (-not $FeedId)
        {
            throw 'Device feed not found by name or ID'
        }
    }

    $Postheaders = @{
        "content-type" = "application/json"
        "X-API-KEY" = "$APIKey"
    }

    $uri = "{0}/apiv2/imports/users/{1}/items" -f $Instance, $FeedId

    foreach($Row in $dataTable)
    {
        $Body = $null
        $Body = $Row | Select-Object * -ExcludeProperty ItemArray, Table, RowError, RowState, HasErrors | ConvertTo-Json
        Invoke-RestMethod -Headers $Postheaders -Uri $uri -Method Post -Body $Body | out-null
        $RowCount++
    }

    Return "$RowCount users added"
}
Export-Modulemember -function Invoke-DwAPIAUploadUserFeedFromAD

function Invoke-DwAPIUploadUserLocationFeedFromAD {
    <#
    .Synopsis
    Pulls user data from Get-ADUser and upload to a DW User Location feed

    .Description
    Takes all users from Get-ADUser (optional server/cred parameters) transforms the fields into a datatable in the format required
    for the DW API and then uploads that user data to a named or numbered data feed.

    .Parameter Instance
    The URI to the Dashworks instance being examined.

    .Parameter APIKey
    The APIKey for a user with access to the required resources.

    .Parameter LocationFeedID
    The id of the location feed to be used.

    .Parameter UserFeedId
    The id of the user feed in question.

    .Parameter ADServer
    The name of a DC to connect Get-ADUser to.

    .Parameter Credentials
    The credentials to use when calling Get-ADUser

    .Outputs
    Output type [string]
    Text confirming the number of rows to be inserted.

    .Example
    # Get the device feed id for the named feed.
    Write-DeviceFeedData -Instance $Instance -DWDataTable $dtDashworksInput -FeedId $DeviceImportID -APIKey $APIKey
    #>


    Param (
        [parameter(Mandatory=$True)]
        [string]$Instance,

        [Parameter(Mandatory=$True)]
        [string]$APIKey,

        [parameter(Mandatory=$True)]
        [string]$LocationFeedID,

        [parameter(Mandatory=$True)]
        [string]$UserFeedId,

        [parameter(Mandatory=$False)]
        [string]$ADServer,

        [parameter(Mandatory=$False)]
        [PSCredential]$Cred
    )

    $Properties = @("StreetAddress","City","State","PostalCode","co")

    if ($ADServer)
    {
        if ($Cred)
        {
            $ADUsers = get-aduser -Filter * -Properties $properties -Server $ADServer -Credential $Cred
        }
        else
        {
            $ADUsers = get-aduser -Filter * -Properties $properties -Server $ADServer
        }
    }else{
        if ($Cred)
        {
            $ADUsers = get-aduser -Filter * -Properties $properties -Credential $Cred
        }
        else
        {
            $ADUsers = get-aduser -Filter * -Properties $properties
        }
    }

    $Locations = @()
    $UserLocations = @{}

    foreach($User in $ADUsers)
    {
        $uniqueIdentifier=$null
        if ("$($User.StreetAddress)$($User.City)$($User.State)$($User.PostalCode)$($User.co)" -ne "")
        {
            $stringAsStream = [System.IO.MemoryStream]::new()
            $writer = [System.IO.StreamWriter]::new($stringAsStream)
            $writer.write("$($User.StreetAddress)$($User.City)$($User.State)$($User.PostalCode)$($User.co)")
            $writer.Flush()
            $stringAsStream.Position = 0
            $uniqueIdentifier = (Get-FileHash -InputStream $stringAsStream | Select-Object -property Hash).Hash

            if ($null -eq $Locations.uniqueidentifier)
            {
                $Location = New-Object PSObject
                $Location | Add-Member -type NoteProperty -Name 'uniqueIdentifier' -Value $uniqueIdentifier
                $Location | Add-Member -type NoteProperty -Name 'name' -Value $(if ($User.StreetAddress -like '*`n*'){($User.StreetAddress -split "`n")[0]}else{$User.StreetAddress})
                $Location | Add-Member -type NoteProperty -Name 'region' -Value "No Region Data"
                $Location | Add-Member -type NoteProperty -Name 'country' -Value $User.co
                $Location | Add-Member -type NoteProperty -Name 'state' -Value $User.State
                $Location | Add-Member -type NoteProperty -Name 'city' -Value $User.City
                $Location | Add-Member -type NoteProperty -Name 'buildingName' -Value $(if ($User.StreetAddress -like '*`n*'){($User.StreetAddress -split "`n")[0]}else{$User.StreetAddress})
                $Location | Add-Member -type NoteProperty -Name 'address1' -Value $(if ($User.StreetAddress -like '*`n*'){($User.StreetAddress -split "`n")[0]}else{$User.StreetAddress})
                $Location | Add-Member -type NoteProperty -Name 'address2' -Value $(if ($User.StreetAddress -like '*`n*'){($User.StreetAddress -split "`n")[1]}else{''})
                $Location | Add-Member -type NoteProperty -Name 'address3' -Value $(if ($User.StreetAddress -like '*`n*'){($User.StreetAddress -split "`n")[2]}else{''})
                $Location | Add-Member -type NoteProperty -Name 'address4' -Value $(if ($User.StreetAddress -like '*`n*'){($User.StreetAddress -split "`n")[3]}else{''})
                $Location | Add-Member -type NoteProperty -Name 'postalCode' -Value $User.PostalCode
                $Locations += $Location
            }
            elseif (-not $Locations.uniqueidentifier.contains($uniqueIdentifier))
            {
                $Location = New-Object PSObject
                $Location | Add-Member -type NoteProperty -Name 'uniqueIdentifier' -Value $uniqueIdentifier
                $Location | Add-Member -type NoteProperty -Name 'name' -Value $(if ($User.StreetAddress -like '*`n*'){($User.StreetAddress -split "`n")[0].Replace("`r","")}else{$User.StreetAddress})
                $Location | Add-Member -type NoteProperty -Name 'region' -Value "No Region Data"
                $Location | Add-Member -type NoteProperty -Name 'country' -Value $User.co
                $Location | Add-Member -type NoteProperty -Name 'state' -Value $User.State
                $Location | Add-Member -type NoteProperty -Name 'city' -Value $User.City
                $Location | Add-Member -type NoteProperty -Name 'buildingName' -Value $(if ($User.StreetAddress -like '*`n*'){($User.StreetAddress -split "`n")[0].Replace("`r","")}else{$User.StreetAddress})
                $Location | Add-Member -type NoteProperty -Name 'address1' -Value $(if ($User.StreetAddress -like '*`n*'){($User.StreetAddress -split "`n")[0].Replace("`r","")}else{$User.StreetAddress})
                $Location | Add-Member -type NoteProperty -Name 'address2' -Value $(if ($User.StreetAddress -like '*`n*'){if($($User.StreetAddress -split "`n")[1]){$($User.StreetAddress -split "`n")[1].Replace("`r","")}else{''}}else{''})
                $Location | Add-Member -type NoteProperty -Name 'address3' -Value $(if ($User.StreetAddress -like '*`n*'){if($($User.StreetAddress -split "`n")[2]){$($User.StreetAddress -split "`n")[2].Replace("`r","")}else{''}}else{''})
                $Location | Add-Member -type NoteProperty -Name 'address4' -Value $(if ($User.StreetAddress -like '*`n*'){if($($User.StreetAddress -split "`n")[3]){$($User.StreetAddress -split "`n")[3].Replace("`r","")}else{''}}else{''})
                $Location | Add-Member -type NoteProperty -Name 'postalCode' -Value $User.PostalCode
                $Locations += $Location
            }
            $UserLocations.Add($($User.SamAccountName),$uniqueIdentifier)
        }
    }

    $JsonLocationArray = @()

    foreach($Location in $Locations)
    {
        $LocUsers = @()
        foreach($User in $ADUsers)
        {
            if($UserLocations[$($User.SamAccountName)] -eq $Location.uniqueIdentifier)
            {
                $LocUsers += "/imports/users/$UserFeedID/items/$($User.SamAccountName)"
            }
        }
        $JSonObject = [pscustomobject]@{
            uniqueIdentifier = $Location.uniqueIdentifier
            name = $Location.name
            region = $Location.region
            country = $Location.country
            state = $Location.state
            city = $Location.city
            buildingName = $Location.buildingName
            address1 = $Location.address1
            address2 = $Location.address2
            address3 = $Location.address3
            address4 = $Location.address4
            postalCode = $Location.postalCode
            users = $LocUsers
        }
        $JsonLocationArray += $JSonObject | ConvertTo-Json
    }

    $RowCount = 0

    $PostHeaders = @{
        "content-type" = "application/json"
        "X-API-KEY" = "$APIKey"
    }

    $DeleteHeaders = @{
        "X-API-KEY" = "$APIKey"
    }

    $uri = "{0}/apiv2/imports/Locations/{1}/items" -f $Instance, $LocationFeedID

    #Prior to insert to the Location data, clear down the existing data.
    Invoke-RestMethod -Headers $DeleteHeaders -Uri $uri -Method Delete

    foreach($Body in $JsonLocationArray)
    {
        Invoke-RestMethod -Headers $PostHeaders -Uri $uri -Method Post -Body $Body | out-null
        $RowCount++
    }

    Return "$RowCount locations added"
}
Export-Modulemember -function Invoke-DwAPIUploadUserLocationFeedFromAD

function Invoke-DwAPIUploadUserDepartmentFeedFromAD {
    <#
    .Synopsis
    Pulls user data from Get-ADUser and upload to a DW User Department feed

    .Description
    Takes all users from Get-ADUser (optional server/cred parameters) transforms the fields into a datatable in the format required
    for the DW API and then uploads that user data to a named or numbered data feed.

    .Parameter Instance
    The URI to the Dashworks instance being examined.

    .Parameter APIKey
    The APIKey for a user with access to the required resources.

    .Parameter DepartmentFeedID
    The id of the Department feed to be used.

    .Parameter UserFeedId
    The id of the user feed in question.

    .Parameter ADServer
    The name of a DC to connect Get-ADUser to.

    .Parameter Credential
    The credentials to use when calling Get-ADUser of type PSCredential.

    .Outputs
    Output type [string]
    Text confirming the number of rows to be inserted.

    .Example
    # Get the device feed id for the named feed.
    Write-DeviceFeedData -Instance $Instance -DWDataTable $dtDashworksInput -FeedId $DeviceImportID -APIKey $APIKey
    #>


    Param (
        [parameter(Mandatory=$True)]
        [string]$Instance,

        [Parameter(Mandatory=$True)]
        [string]$APIKey,

        [parameter(Mandatory=$True)]
        [string]$DepartmentFeedID,

        [parameter(Mandatory=$True)]
        [string]$UserFeedId,

        [parameter(Mandatory=$False)]
        [string]$ADServer,

        [parameter(Mandatory=$False)]
        [PSCredential]$Credential
    )

    $Properties = @("Department","Company")

    if ($ADServer)
    {
        if ($Credential)
        {
            $ADUsers = get-aduser -Filter * -Properties $properties -Server $ADServer -Credential $Credential
        }
        else
        {
            $ADUsers = get-aduser -Filter * -Properties $properties -Server $ADServer
        }
    }else{
        if ($Credential)
        {
            $ADUsers = get-aduser -Filter * -Properties $properties -Credential $Credential
        }
        else
        {
            $ADUsers = get-aduser -Filter * -Properties $properties
        }
    }

    $Departments = @()
    $UserDepartments = @{}

    foreach($User in $ADUsers)
    {
        $uniqueIdentifier=$null
        $CompanyUID=$null

        if ("{0}{1}" -f $User.Company, $User.Department -ne "")
        {
            $stringAsStream = [System.IO.MemoryStream]::new()
            $writer = [System.IO.StreamWriter]::new($stringAsStream)
            $writer.write("$($User.Company)#$($User.Department)")
            $writer.Flush()
            $stringAsStream.Position = 0
            $uniqueIdentifier = (Get-FileHash -InputStream $stringAsStream | Select-Object -property Hash).Hash

            if ($User.Company)
            {
                $stringAsStream = [System.IO.MemoryStream]::new()
                $writer = [System.IO.StreamWriter]::new($stringAsStream)
                $writer.write($User.Company)
                $writer.Flush()
                $stringAsStream.Position = 0
                $CompanyUID = (Get-FileHash -InputStream $stringAsStream | Select-Object -property Hash).Hash
            }

            if ($null -eq $Departments.uniqueidentifier)
            {
                $Department = New-Object PSObject
                $Department | Add-Member -type NoteProperty -Name 'uniqueIdentifier' -Value $uniqueIdentifier
                $Department | Add-Member -type NoteProperty -Name 'Department' -Value $User.Department
                $Department | Add-Member -type NoteProperty -Name 'CompanyUID' -Value $CompanyUID
                $Department | Add-Member -type NoteProperty -Name 'Company' -Value $User.Company
                $Departments += $Department
            }
            elseif (-not $Departments.uniqueidentifier.contains($uniqueIdentifier))
            {
                $Department = New-Object PSObject
                $Department | Add-Member -type NoteProperty -Name 'uniqueIdentifier' -Value $uniqueIdentifier
                $Department | Add-Member -type NoteProperty -Name 'Department' -Value $User.Department
                $Department | Add-Member -type NoteProperty -Name 'CompanyUID' -Value $CompanyUID
                $Department | Add-Member -type NoteProperty -Name 'Company' -Value $User.Company
                $Departments += $Department
            }
            $UserDepartments.Add($($User.SamAccountName),$uniqueIdentifier)
        }
    }

    $JsonDepartmentArray = @()
    $SeenCompanies = @()
    $SeenCompanies += ''

    foreach($Department in $Departments)
    {
        $DeptUsers = @()
        foreach($User in $ADUsers)
        {
            if($UserDepartments[$($User.SamAccountName)] -eq $Department.uniqueIdentifier)
            {
                $DeptUsers += "/imports/users/$UserFeedID/items/$($User.SamAccountName)"
            }
        }
        if (-not $SeenCompanies.Contains($Department.CompanyUID))
        {
            $JSonObject = [pscustomobject]@{
                uniqueIdentifier = $Department.CompanyUID
                name = $Department.Company
            }
            $JsonDepartmentArray += $JSonObject | ConvertTo-Json
        }
        $JSonObject = [pscustomobject]@{
            uniqueIdentifier = $Department.uniqueIdentifier
            name = $Department.Department
            parentUniqueIdentifier = $Department.CompanyUID
            users = $DeptUsers
        }
        $JsonDepartmentArray += $JSonObject | ConvertTo-Json
    }

    $RowCount = 0

    $PostHeaders = @{
        "content-type" = "application/json"
        "X-API-KEY" = "$APIKey"
    }

    $DeleteHeaders = @{
        "X-API-KEY" = "$APIKey"
    }

    $uri = "{0}/apiv2/imports/Departments/{1}/items" -f $Instance, $DepartmentFeedID

    #Prior to insert to the Location data, clear down the existing data.
    Invoke-RestMethod -Headers $DeleteHeaders -Uri $uri -Method Delete

    foreach($Body in $JsonLocationArray)
    {
        Invoke-RestMethod -Headers $PostHeaders -Uri $uri -Method Post -Body $Body | out-null
        $RowCount++
    }

    Return "$RowCount locations added"
}
Export-Modulemember -function Invoke-DwAPIUploadUserDepartmentFeedFromAD