The following script spits out all UserProfile properties for users on Sharepoint 2007:
[void][System.Reflection.Assembly]::LoadWithPartialName("Microsoft.Office.Server")
[void][System.Reflection.Assembly]::LoadWithPartialName("Microsoft.Office.Server.UserProfiles")
# Function: Get-UserProfiles
# Description: return a UserProfileManager object containing all user profiles
# Parameters: SSPName SSPName
#
Function global:Get-UserProfiles($SSPName)
{
$ServerContext = [Microsoft.Office.Server.ServerContext]::GetContext($SSPName);
$UPManager = new-object Microsoft.Office.Server.UserProfiles.UserProfileManager($ServerContext);
return $UPManager.GetEnumerator();
}
$profiles = Get-UserProfiles("SharedServices");
$profiles | ForEach-Object { $_.GetEnumerator();}
However, what I want to do is be able to return a table, or csv file of specific values in the profile, e.g. Username, WorkEmail, WorkPhone. I have tried piping the output to |ft Username, WorkEmail, Workphone and | select Username, WorkEmail, WorkPhone but this just returns blanks.
I feel like I am so close. I don't want to replace the $_.GetEnumerator() call with lots of $_.Item("property") calls and it doesn't feel like I should have to. Any ideas?
I've further developed the code so that it now accepts a comma delimited list of properties and writes them to a delimited file.
# Outputs a delimited file with specified user profile properties for each user in Sharepoint
# Create array of desired properties
$arProperties = 'UserName','FirstName','LastName','Title','WorkEmail','WorkPhone','Manager','AlternateContact','RoleDescription','PictureURL';
# Specify output file
$outfile = 'UserProfiles.csv';
#Specify delimiter character (i.e. not one that might appear in your user profile data)
$delim = '^';
# Specify Shared Service Provider that contains the user profiles.
$SSP = "SharedServices";
[void][System.Reflection.Assembly]::LoadWithPartialName("Microsoft.Office.Server")
[void][System.Reflection.Assembly]::LoadWithPartialName("Microsoft.Office.Server.UserProfiles")
# Function: Get-UserProfiles
# Description: return a UserProfileManager object containing all user profiles
# Parameters: SSPName SSPName
#
Function global:Get-UserProfiles($SSPName)
{
$ServerContext = [Microsoft.Office.Server.ServerContext]::GetContext($SSPName);
$UPManager = new-object Microsoft.Office.Server.UserProfiles.UserProfileManager($ServerContext);
return $UPManager.GetEnumerator();
}
$profiles = Get-UserProfiles($SSP);
#Initialise Output file with headings
$header = [string]::join($delim,$arProperties);
Write-Output $header | Out-File $outfile
#Output the specified properties for each
$profiles | ForEach-Object {
foreach($p in $arProperties){
# Get the property name and add it to a new array, which will be used to construct the result string
$arProfileProps += $_.Item($p);
}
$results = [string]::join($delim,$arProfileProps);
# Get rid of any newlines that may be in there.
$CleanResults = $results.Replace("`n",'');
Write-Output $CleanResults
Remove-Variable -Name arProfileProps
} | Out-File -Append $outfile
This gets me a bit closer. I'd still really like a script that iterates through all the profile properties and puts them into a CSV or XML file more gracefully. This will do for now.
Related
I need to get all the parent hierarchy from a work item.
The parent of the parent of the parent.. find all the parent links.
There is a way to do it via API?
Thanks :)
Yes, you can.
But you must use code to handle this situation. The below is a Python demo.
from azure.devops.connection import Connection
from msrest.authentication import BasicAuthentication
#get all the work items linked to a work item
def get_work_items_parents(Organization_Name, personal_access_token, wi_id):
#get a connection to Azure DevOps
organization_url = 'https://dev.azure.com/'+Organization_Name
credentials = BasicAuthentication('', personal_access_token)
connection = Connection(base_url=organization_url, creds=credentials)
work_item_tracking_client = connection.clients.get_work_item_tracking_client()
work_item = work_item_tracking_client.get_work_item(wi_id, expand="relations")
#get the work item links
for item in work_item.relations:
#get parent and child work items
if item.attributes['name'] == 'Parent':
#get the work item id
work_item_id = item.url.split('/')[-1]
#get the work item
linked_work_item = work_item_tracking_client.get_work_item(work_item_id)
#add the work item to the list
work_items_parents.append(linked_work_item)
#get the parents of the parent
get_work_items_parents(Organization_Name, personal_access_token, work_item_id)
return work_items_parents
personal_access_token = 'xxx'
Organization_Name = 'xxx'
workitem_id = 120
#create a list
work_items_parents = []
items = get_work_items_parents(Organization_Name, personal_access_token, workitem_id)
for item in items:
print(item.fields['System.Title'])
I can successfully get all parents of workitem id '120':
The above is the API of Python SDK Demo, if you want to use REST API, I can also give you an idea.
Just call this REST API:
https://dev.azure.com/<Organization Name>/<Project Name>/_apis/wit/workitems/<Workitem ID>?api-version=6.0&$expand=relations
You can call it again after getting the current parent:
Official document of the REST API:
Work Items - Get Work Item
Try the following PowerShell script: (In this sample, the work item 2434is the last parent work item - top1 parent, 2435 is top2 parent ...)
Param(
[string]$orgurl = "https://dev.azure.com/{org}",
[string]$project = "Artifacts",
[string]$workitemid = "2527", #The specific child work item ID
[string]$user = "",
[string]$token = "PAT"
)
# Base64-encodes the Personal Access Token (PAT) appropriately
$base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f $user,$token)))
#Get all parent hierarchy from a specific work Item
cls
$parenturl = "$orgurl/$project/_apis/wit/workItems/$($workitemid)?"+"$"+"expand=relations&api-version=6.0"
$parentwi = #()
Do {
$wiresults = Invoke-RestMethod -Uri $parenturl -Method Get -Headers #{Authorization=("Basic {0}" -f $base64AuthInfo)}
$wirelations = $wiresults.relations | where{$_.rel -eq 'System.LinkTypes.Hierarchy-Reverse'}
$parentwi += $wiresults.id
$wiurl = $wirelations.url
#Print the parent work item link
Write-Host $wiurl
$parenturl = "$wiurl"+"?"+"$"+"expand=relations&api-version=6.0"
}
While($wiurl)
#Print the work items hierarchy Child -> Parent
Write-Host "########################################"
Write-Host "Work Items Hierarchy : Child -> Parent"
Write-Host "########################################"
$parentwi
# main.tf
data "external" "extDateTime" {
program = ["pwsh", "${path.module}/getDateTime.ps1"]
}
output "value" {
value = "${data.external.extDateTime.result.dateTime}"
}
This is the Powershell file getDateTime.ps1 code section
# getDateTime.ps1
$DateTime = Get-Date -Format "yyyyMMddHHmmss"
Write-Output "{""dateTime"": $DateTime}"
Then I run the following command: terraform plan
Error: Unexpected External Program Results with data.external.extDateTime, on main.tf line 26, in data "external" "extDateTime":
26: program = ["Powershell.exe", "${path.module}/getDateTime.ps1"]
The data source received unexpected results after executing the program. Program output must be a JSON encoded map of string keys and string values.
Program: C:\WINDOWS\System32\WindowsPowerShell\v1.0\Powershell.exe
Result Error: invalid character '{' after top-level value
My understanding is the PS script has to return in JSON format but I keep getting the Result Error. Any ideas would be appreciated.
Ensure that the JSON values are correctly formatted as strings (using back ticks to escape double quotes):
$DateTime = Get-Date -Format "yyyyMMddHHmmss"
Write-Output "{`"dateTime`": `"$DateTime`"}"
As per the Terraform docs for the external data source:
The JSON object contains the contents of the query argument and its values will always be strings.
Another way that this can be done is to create a PowerShell custom object and convert it to JSON:
$DateTime = Get-Date -Format "yyyyMMddHHmmss"
$myObject = [PSCustomObject]#{
dateTime = $DateTime
}
ConvertTo-Json $myObject
If you need the output to be a number, you can use the tonumber() function:
output "value" {
value = tonumber(data.external.extDateTime.result.dateTime)
}
I figured out what the issue was. I was running terraform commands in PS 7 (pwsh.exe) but my data.external program was calling PS 5 (powershell.exe). Once I updated to the following: program = ["pwsh.exe", "${path.module}/getDateTime.ps1"] it worked fine. The error message referring to the { was misleading me. Thanks again Cody.
What I'm trying to do
The below script loops through every item in an Array of data streams and requests a summary value for output to a text file. This external request is by far the most expensive part of the process, and so I am now using a Runspacepool to run multiple (5) requests in parallel, and whichever finishes first outputs its results.
These requests all write to a synchronised hashtable, $hash, which holds a running total ($hash.counter) and tracks which thread ($hash.thread) is updating the total and a .txt output file, to avoid potential write collisions.
What isn't working
Each thread is able to update the counter easily enough $hash.counter+=$r, but when I try and Read the value into an Add-Content statement:
Add-Content C:\Temp\test.txt "$hash.counter|$r|$p|$ThreadID"
it adds an object reference rather than a number:
System.Collections.Hashtable+SyncHashtable.counter|123|MyStreamName|21252
And so I've ended up passing the counter through a temporary variable that can be used in the string:
[int]$t = $hash.counter+0
Add-Content C:\Temp\test.txt "$t|$r|$p|$ThreadID"
Which does output the true total:
14565423|123|MyStreamName|21252
What I'm asking
Is it possible to remove this temporary variable and output directly from the hashtable? Why does the object reference have a '+' in the middle?
I've had to add logic to 'lock' the hashtable to prevent data collisions. Should this be necessary? I'd been told that synchronised hashtables were supposed to be thread-safe for R/W operations, but without this logic my counter doesn't reach the correct total.
Full code for the loop itself below - I've left out setup of the Runspacepool etc
ForEach($i in $Array){
# Save down the data stream name and create parameter list for passing to the new job
$p = $i.Point.Name
$parameters = #{
hash = $hash
conn = $Conn
p = $p
}
# Instantiate new powershell runspace and send a script to it
$PowerShell = [powershell]::Create()
$PowerShell.RunspacePool = $RunspacePool
[void]$Powershell.AddScript({
# Receive parameter list, retrieve threadid
Param (
$hash,
$conn,
$p
)
$ThreadID = [appdomain]::GetCurrentThreadId()
# Send data request to the PI Data Archive using the existing connection
$q = Get-something (actual code removed)
[int]$r = $q.Values.Values[0].Value
# Lock out other threads from writing to the hashtable and output file to prevent collisions
# If the thread isn't locked, attempt to lock it. If it is locked, sleep for 1ms and try again. Tracked by synchronised Hashtable.
Do{
if($hash.thread -eq 0){
$hash.thread = $ThreadID
}
# Sleep for 1ms and check that the lock wasn't overwritten by another parallel thread.
Start-Sleep -Milliseconds 1
}Until($hash.thread -eq $ThreadID)
# Increment the synchronised hash counter. Save the new result to a temporary variable (can't figure out how to get the hash counter itself to output to the file)
$hash.counter+=$r
[int]$t = $hash.counter+0
# Write to output file new counter total, result, pointName and threadID
Add-Content C:\Temp\test.txt "$t|$r|$p|$ThreadID"
# release lock on the hashtable and output file
$hash.thread = 0
})
# Add parameter list to instance (matching param() list from the script. Invoke the new instance and save a handle for closing it
[void]$Powershell.AddParameters($parameters)
$Handle = $PowerShell.BeginInvoke()
# Save down the handle into the $jobs list for closing the instances afterwards
$temp = [PSCustomObject]#{
PowerShell=$Powershell
Handle=$Handle
}
[void]$jobs.Add($Temp)
}
Keep in mind I'm new to this and be gentle.
I have a full file path for a document "C:\folder1\folder2\01.03.2017 - FileName.csv" and I want to manipulate it to return the dir that the file is stored in (C:\folder1\folder2), minus the filename (01.03.2017 - FileName.csv).
I'm trying to make this modular so that it will work regardless of the amount of sub-folders a file sits in; we also won't know the FileName in advance, so again this needs to be modular and remove up to and including the last "\"
For background info on how this is currently built, I nicked a bit of code from a previous question I saw on StackOverflow:
Function Get-FileName($initialDirectory)
{
[System.Reflection.Assembly]::LoadWithPartialName(“System.windows.forms”) |
Out-Null
$OpenFileDialog = New-Object System.Windows.Forms.OpenFileDialog
$OpenFileDialog.initialDirectory = $initialDirectory
$OpenFileDialog.filter = “All files (*.*)| *.*”
$OpenFileDialog.ShowDialog() | Out-Null
$OpenFileDialog.filename
} #end function Get-FileName
# *** Entry Point to Script ***
$originalData = Get-FileName -initialDirectory “c:\” | Out-String
Write-Host $originalData
$originalDir = $originalData.Split('\')
$originalDir
Running this currently prompts for an "open dialog box" you would see in Windows. You select a folder and the output is currently:
C:\folder1\folder2\01.03.2017 - FileName.csv
C:
folder1
folder2
01.03.2017 - FileName.csv
I've tried a few different -join attempts but none successful.
We will have an input of C:\folder1\folder2\01.03.2017 - FileName.csv as a variable $originalData.
We want the output to be C:\folder1\folder2 as a variable $originalDir.
Function Get-FileName($initialDirectory)
{
[System.Reflection.Assembly]::LoadWithPartialName(“System.windows.forms”) |
Out-Null
$OpenFileDialog = New-Object System.Windows.Forms.OpenFileDialog
$OpenFileDialog.initialDirectory = $initialDirectory
$OpenFileDialog.filter = “All files (*.*)| *.*”
$OpenFileDialog.ShowDialog() | Out-Null
$OpenFileDialog.filename
} #end function Get-FileName
$originalData = Get-FileName -initialDirectory “c:\”
Write-Host $originalData
$originalDir = (Get-ChildItem $originalData).DirectoryName
you can use it like so, so take result or your function and use it with get-childitem.
edit: notice there's no | Out-String on the third to last line
I am using a Powershell script to write to a text file. A client showed me this Powershell script to use to replace a excel macro I used to use...
$computers= gc "C:\Users\Powershell\DeviceList.txt"
foreach ($computername in $computers)
{
write-output "<$computername>
active = yes
group =
interval = 5min
name = $computername
host = $computername
community =
version = 1
timeout = 0
retries = default
port = 161
qos_source = 1
</$computername>" | Out-File -filepath "C:\Users\Powershell\Cisco_Mon.txt" -append
}
It works great but now I wanted to build on it to add additional variables. In a perfect world I would like it to read from an excel spreadsheed grabbing each rowof data and each column being defined as a variable. For now using another text file is fine as well. Here is what I started with (it doesnt work) but you can see where I am going with it...
$computers= gc "C:\Users\Powershell\devicelist.txt"
$groups= gc "C:\Users\Powershell\grouplist.txt"
foreach ($computername in $computers) + ($groupname in $groups)
{
write-output "<$computername>
active = yes
group = $groupname
interval = 5min
name = $computername
host = $computername
community =
version = 1
timeout = 0
retries = default
port = 161
qos_source = 1
</$computername>" | Out-File -filepath "C:\Users\Powershell\Cisco_Mon.txt" -append
}
Of course it is not working. Essentially I would LOVE it if I could define each of the above options into a variable from an excel spreadsheet, such as $community, $interval, $active, etc.
Any help with this would be very much appreaciated. If someone could show me how to use an excel spreadsheet, have each column defined as a variable, and write the above text with the variables, that would be GREAT!!!.
Thanks,
smt1228#gmail.com
An Example of this would be the following...
Excel Data: (Colums seperated with "|"
IP | String | Group
10.1.2.3 | Public | Payless
Desired Output:
<10.1.2.3>
active = yes
group = Payless
interval = 5min
name = 10.1.2.3
host = 10.1.2.3
community =
version = 1
timeout = 0
retries = default
port = 161
qos_source = 1
</10.1.2.3>
Addition:
Pulling data from CSV for IP, String, Group where data is as follows in CSV...
10.1.2.3,public,group1
10.2.2.3,default,group2
10.3.2.3,public,group3
10.4.2.3,default,group4
to be writting into a .txt file as
IP = 10.1.2.3.
String = public
Group = Group1
and look for each line in the CSV
Ok, new answer now. The easiest way would be to save your Excel document as CSV so that it looks like this (i.e. very similar to how you presented your data above):
IP,String,Group
10.1.2.3,Public,Payless
You can still open that in Excel, too (and you avoid having to use the Office interop to try parsing out the values).
PowerShell can parse CSV just fine: with Import-Csv:
Import-Csv grouplist.csv | ForEach-Object {
"<{0}>
active = yes
group = {1}
interval = 5min
name = 10.1.2.3
host = 10.1.2.3
community =
version = 1
timeout = 0
retries = default
port = 161
qos_source = 1
</{0}>" -f $_.IP, $_.Group
}
I'm using a format string here where {0}, etc. are placeholders. -f then is the format operator which takes a format string on the left and arguments for the placeholders on the right. You can also see that you can access the individual columns by their name, thanks to Import-Csv.