Create File Folder and permission

I needed a quick script to include in my runbooks – one to create the home drive for my new user process. I got the base for this off the internet ( https://activedirectoryfaq.com/2017/09/powershell-create-home-directory-grant-permissions/ ) but I wanted to add a little bit of checking to see if the file existed first, etc.

param([Parameter(Mandatory=$true)][String]$samAccountName)

$User = Get-ADUser -Identity $samAccountName -Properties l

$office = $User.l

$homePath = "\\my.netork\user$\" + $Office + "\{0}" -f $samAccountName

 
if($User -ne $Null) {
	
	#check home path to make sure one doesn't already exist
	[string]$homepathtest = Test-Path $homePath
	
	If ($homepathtest -eq "False") 
		{
		#create home drive
	    $homeShare = New-Item -path $homePath -ItemType Directory -force -ea Stop
	 	
	    $acl = Get-Acl $homeShare
	 	
		#permissison home drive
	    $FileSystemRights = [System.Security.AccessControl.FileSystemRights]"Full"
	    $AccessControlType = [System.Security.AccessControl.AccessControlType]::Allow
	    $InheritanceFlags = [System.Security.AccessControl.InheritanceFlags]"ContainerInherit, ObjectInherit"
	    $PropagationFlags = [System.Security.AccessControl.PropagationFlags]"InheritOnly"
	 
	    $AccessRule = New-Object System.Security.AccessControl.FileSystemAccessRule ($User.SID, $FileSystemRights, $InheritanceFlags, $PropagationFlags, $AccessControlType)
	    $acl.AddAccessRule($AccessRule)
	 
	    Set-Acl -Path $homeShare -AclObject $acl -ea Stop
	 	
	    Write-Host ("HomeDirectory created at {0}" -f $homePath)
	
		}
		
	If($homepathtest -eq "true") {
		Write-Host ("Home Directory at {0} already exists" -f $homePath )
		}
	
#
} 

Simple Script to Dump the members of many groups named similarly

param([Parameter(Mandatory=$true)]$GroupPrefix)
# Just specify the group prefix and the script will query all groups and create a csv file named with the prefix in the temp folder.

# This has a dependancy on Quest Powershell tools
add-pssnapin Quest.ActiveRoles.ADManagement -ErrorAction SilentlyContinue

# get the data
Get-QADGroup -Name "$GroupPrefix*" | foreach {$group = $_; Get-QADGroupMember -Identity $_ } | Get-QADUser | foreach {New-Object PSObject -Property @{Group=$group.Name; GroupDesc=$group.Description; SamAccountName=$_.SamAccountName;LastName=$_.LastName;FirstName=$_.FirstName;Email=$_.email;DisplayName=$_.DisplayName;Department=$_.Department; DN=$_.DN; Manager=$_.Manager}} | Export-Csv c:\TEMP\$GroupPrefix.csv

$temp = $null
$temp = Import-Csv c:\TEMP\$GroupPrefix.csv

Powershell to Create TFS Backlog Items

I work with Team foundation server – keeping track of projects, etc… and now that it’s January i had 100+ tasks to create. I thought to myself… why do this manually?


####################################################################
###
###  Script to create TFS entries from CSV file
###  by Jeremy Castleberry 
###  created 1-16-2019
###  last update 1-17-2019
###  Version 1
###
####################################################################

#region Log Of Changes
####################################################################
###
###  
###  
###
###
###
###
####################################################################
#endregion Log of Changes
####################################################################


#region FUNCTIONS

#############################################################################
##
## Function from Hey Scripting Guys!
##  https://blogs.technet.microsoft.com/heyscriptingguy/2009/09/01/hey-scripting-guy-can-i-open-a-file-dialog-box-with-windows-powershell/
##
#############################################################################

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


#endregion FUNCTIONS


#region Get Input File
#We need to prompt for the file. 

[string]$VARfilepath  = Get-FileName

# or set it hardcoded
#$VARfilepath = "c:\temp\tasks.csv"

#EndRegion Get Input File
####################################################################

#region Modules
# Import whichever modules we need
Import-Module TfsCmdlets -Force

#endregion Modules
####################################################################

#region Variables
[string]$VARUrl = "http://YOURSERVER:####/tfs/Projects/"
[string]$VARTFSproject = "TFSPROJECT"
[array]$ARRTFSItems = @()

#endregion Variables
####################################################################

#region Connect to TFS components

#First to the server
Connect-TfsTeamProjectCollection $VARUrl

# and now our project 
Connect-TfsTeamProject -Project $VARTFSproject
$OBJtms = Get-TfsTeamProject -Project $VARTFSproject

# If you need an Array of projects:
#$ARRprojects = Get-TfsTeamProject

# If you need an array of sprints:
#$ARRIterations = Get-TfsIteration -Project TFSPROJECT

#endregion Connect to TFS components
####################################################################

#Region BODY
####################################################################
###
###  Body
###
####################################################################

#############################
## Get the Array of Tasks


$ARRTFSItems = Import-Csv -Path $VARfilepath

# List of columns expected in the CSV:
# areaPath
# iterationPath
# assignee
# title
# type    #'Task' or 'Product Backlog Item'
# description
# parentItem
# $effort
# tags

## Start working through each of our TFS items and Create them.

Foreach ($tfsitem in $ARRTFSItems)
	{
	####################################################################
	# Let's blank out the variables.  
	# if a property is "empty" in the array,  when looping the variable isn't replaced by setting the variable
	# to the property and so the previous use of that property for the pevious item in the loop is kept.  
	####################################################################
	
	$areaPath = $null
	$iterationPath = $null
	$assignee = $null
	$title = $null
	$type = $null     #'Task' or 'Product Backlog Item'
	$description = $null
	$parentItem = $null
	$effort = $null
	$tags = $null
	
	## now we set the variables to the data in our TFS item in the Array
	
	$areaPath = $tfsitem.areaPath
	$iterationPath = $tfsitem.iterationPath
	$assignee = $tfsitem.assignee
	$title = $tfsitem.title
	$type = $tfsitem.type
	$description = $tfsitem.description
	$effort = $tfsitem.effort
	$tags = $tfsitem.Tags
	$parentID = $null
	
	####################################################################
	# for the parent we need to do some custom work.
	# if the item Type is a Backlog Item, then the parent is a #number of a feature. 
	# if the item type is a task - we need to get the backlog Item and it's number
	####################################################################
	
	If ($type -eq "Task")
		{
		#ok. so it's a task.  We need to get the product backlog item from the specified title
		$parenttitle = $tfsitem.parentItem
				
		#we are going to look for the Title in TFS work items
		[array]$ARRparentworkitem = Get-TfsWorkItem -Text $parenttitle
				
		#we most likely will get back multiple so lets loop through and find just the backlog item
		
		Foreach ($parentworkItem in $ARRparentworkitem)
			{
			$parentworkitemType = $parentworkitem.type.Name
			
			If ($parentworkitemType -eq "Product Backlog Item")
				{
				
				$parentworkitemTitle = $parentworkitem.title
				# and lets see if the backlog item is an exact title match
					If ($parentworkitemTitle -eq $parenttitle)
						{
						#ok we have our match - so lets set the Parent
						[int]$parentID = $parentworkitem.Id
					
						#close the IF
						}
				
				#Close the IF
				}
				
			#close the ForEach
			}
		
		
		#close the IF
		}
	
	If ($type -eq "Product Backlog Item")
		{
		#ok. so it's a Product Backlog Item - meaning we should have the Parent ID 
	
		[int]$parentID = $tfsitem.parentItem
		}
			
		
	


	####################################################################
	#
	# Ok. Lets create the object now
	#
	####################################################################
	
	# we create an essentially blank new item

	$newTFSItem = New-TfsWorkItem -Title $title -Type $type -Project $VARTFSproject
		
	# so we need the ID of the newTFSItem

	[int]$Newid = $newTFSItem.Id

	####################################################################
	#
	# Lets build a hash table of the properties
	#
	####################################################################
	
	# we need on set for Backlog Items and another for Tasks

	If ($type -eq "Task")
		{
		
		$HASHUpdateFields = @{
			'Description'=$description
			'Assigned To'=$assignee
			'Iteration Path'=$IterationPath
			'Area Path'=$areaPath
			'Remaining Work'=$effort
			'Tags'=$tags
			}	
		
		#close IF
		}
		
	If ($type -eq "Product Backlog Item")
		{	
		$HASHUpdateFields = @{
			'Description'=$description
			'Assigned To'=$assignee
			'Iteration Path'=$IterationPath
			'Area Path'=$areaPath
			'Effort'=$effort
			'Tags'=$tags
			}
		
		#close IF
		}

	####################################################################
	#
	# Using the SET command we are going to set the hash table to the NewID
	#
	####################################################################
	
	Set-TfsWorkItem -WorkItem $Newid -Fields $HASHUpdateFields


	####################################################################
	#
	# And now we need to connect it to it's parent.
	# TARGET is the PARENT 
	# SOURCE is the CHILD
	#
	####################################################################
	
	# lets make sure we have a parent before we link
	
	If ($parentID -ne $null)
		{
		Add-TfsWorkItemLink -SourceWorkItem $Newid -TargetWorkItem $parentID -EndLinkType Parent	
		
		#Close IF
		}
	
	#close the ForEach
	}	

#endregion Body

A list of columns for the CSV Follows:

  • areaPath
  • iterationPath
  • assignee
  • title
  • description
  • effort
  • tags
  • type
    • Task
    • Product Backlog Item
  • parentItem
    • for Product Backlog Items it should be the # for the Parent Feature
    • for task put the name of the Product Backlog items – which hopefully are unique…. go make it unique


Cool tool for the Cireson Portal

The Cireson portal has it’s own web based pages for Knowledge articles – but what does one do when they have 4000 knowledge articles in Rich Text Format (RTF) stored in the SCSM knowledge base?

A guy named John Doyle was awesome and wrote a script and DLL to migrate the date! Go John! you are the hero of the day.


The other day, someone asked about migrating KB articles from the SCSM database to the Cireson Portal. 

I built a small PowerShell script to attempt this. The code is fairly simple. You need to place the MarkupConverter.dll file somewhere on the file system and then reference this path in the script. You also need to set the URL to your Portal Server, and your credentials to authenticate to the portal.

It uses SMLets to get the list of KB articles from the SCSM Server and then uploads the articles to the Cireson Portal using the API – AddOrUpdateHTMLKnowledgeApi.

The code converts the RTF content in the End User and Analyst content to XAML, and then converts this to HTML. I looks alright on my server, but I am not guaranteeing the accuracy of the conversion.

Please feel free to modify it and adapt it to your needs.


https://us.v-cdn.net/6026663/uploads/editor/8c/2q7dwesg4hb7.zip

So of course i wanted to know more about this “AddorUpdateHTMLKnowledgeAPI which lead me to here:


https://support.cireson.com/Help/Api/POST-api-V3-KnowledgeBase-AddOrUpdateHTMLKnowledgeApi

and of course the general Cireson help:

https://support.cireson.com/Help

Commenting your Code

There is nothing more frustrating than looking at Powershell code from someone else – who didn’t bother to comment the code. 1000 lines without a clue of what is happening, unless you understand the relevance of each command in context. Uhg.

In Powershell one can use the # symbol (Pound or Hashtag) to denote information that is “notes” and not part of the code.

For example I will denote section headers with large blocks of # signs for visibility:

############################################
#
# This section is about commenting your code
#
############################################

and smaller notes with a smaller comment:

####################
## Loop through this

You can also add a # at the end to comment out a piece – for example if I use a whatif for testing but don’t want it there once the code is “production”.

$user = Get-Aduser -name $inputname #-whatif

NOTE: Whereever the #starts, the code ends.

In additon you can use a “Regions” to denote sections of your code. Like college football conferences… but with less gerrymandering.

To do this we use the # sign and the word “region” followed by a description to start the section:

#region Our Variables

And then # and “endregion” to end the section.

#endregion

Most powershell editors, like PowerGui Script editor, will then allow you to collapse the region – makes browsing over sections of code:

I hope you find this useful.

SCSM Entity Explorer

Historically I have used the SMLets to query objects in SCSM and learn about classes and relationships. Powershell works – but it’s not always the most beautiful of interfaces.

I stumbled upon a blog article and SCSM Entity Explorer and I really can’t do it justice. There are a number of well written blog articles, so i am not going to bore you with reading mine – and I am going to replicate the info to find the articles here with the express understanding that please give credit where due.

The tool can be used to explore classes and relationships of objects in SCSM. This is invaluable for learning HOW Service Manager is structured… especially if like my company, you have done some customization on top of the standard management packs.

The tool can be located here:
https://gallery.technet.microsoft.com/SCSM-Entity-Explorer-68b86bd2

It was written by Dieter Gasser who’s blog can be found here:
https://blog.dietergasser.com/2014/05/08/scsm-entity-explorer/


And Xaptiy did an excellent job of explaining the tools use here: https://www.xapity.com/single-post/2017/05/27/How-to-use-SCSM-Entity-Explorer



Service Manager – Which user?

One of the challenges when working with MS Service Manager 2012/2016 is that while there are related user types for things like the affected user (for whom the ticket is related) or the portal user (who put the ticket in), etc – most of the questions in which you choose an Active directory user – are simply related as “related to” and thus – if you as for multiple, you are not 100% sure which is the answer to the question asked.

I find myself frequently looking for a user specified in the questions in my powershell script.

Now one way to determine the actual user is to parse the XML from the answers block and get the name and the question. Match it up and you are good to go.

Anthony Watherson over at microsoft did a good writeup on that process – so I will just link that here:

https://blogs.technet.microsoft.com/automagically/2014/11/16/dealing-with-xml-inputs-from-service-manager/

However – I use something else – Not because it’s necessarily “better” but simply because it popped into my head when i was working on it – and I didn’t google for the XML solution until a much later date… i.e. when I was looking to talk about this content.

For me – I use Cireson’s “multiple mapping” feature to map the name of the user chosen, to a property in the SR:

and eventually handed off to SCORCH and the runbook :

So that gives us the NAME of the user chosen.. but how do we make sure it’s the right user? We might have two John Smiths of Sameer Kumars after all.

We pass that information along the chain to a SCORCH workflow:




The script grabs the user from the name – and passes it to the get object which then returns it to be used by whatever workflow you might need to use that information in.

here is the Powershell:

#
Find Related User
#
Import-Module SMLETS
$SCSMServer = "scsmserver.app.intranet"
$SCSMServer = "localhost"
Set base variables
$SR = "PASSED IN VARIABLE"
$name = "PASSED IN VARIABLE"
$SR = "SR172196"
Establish GUIDS:
$RelationShipType = "System.WorkItemRelatesToConfigItem"
Establish relationship classes
$RouteToRelationship = Get-SCSMRelationshipClass -Name $($RelationShipType +"$") -ComputerName $SCSMServer
$SystemWorkItemAssignedToUserClass = Get-SCSMRelationshipClass -Name System.WorkItemAssignedToUser$ -ComputerName $SCSMServer
Get Service Request
$ServiceRequest = Get-SCSMObject -Class (Get-SCSMClass -Name System.WorkItem.ServiceRequest$ -ComputerName $SCSMServer) -ComputerName $SCSMServer -Filter "ID -eq $SR"
Get user Object
$RoutedToUser = (Get-SCSMRelationshipObject -BySource $ServiceRequest -ComputerName $SCSMServer | ?{$_.RelationshipID -eq $RouteToRelationship.Id -and $_.IsDeleted -eq $False})
#
Foreach ($relateduserobj in $RoutedToUser)
{
$smuserobj = $relateduserobj.TargetObject
If ($smuserobj.displayname -eq $Name)
{
$Global:Userobject = $smuserobj
}
}
$SAM = $Userobject.Name
$ArrSAM = $SAM.Split(".")
#
$outSAM = $ArrSAM[1]
$outname = $Userobject.DisplayName
$outGUID = $Userobject.IdNow this may not be useful to you... b

We use this because we already have it. I was easy to put together when I was looking for a solution. Maybe it’s also useful to someone else… if not… I thank you for at least reading my ramblings 🙂

A better portal..

One of the big limitations that Service manager has is gathering information on requests. In my company we utilize a third party browser based “head” for SCSM requests by a company called Cireson.

https://cireson.com/

Those guys all used to work for Microsoft and do good stuff. If you need any details – just reach out and ask.

JSON – Better than CSV?

If you need to know why, you could find articles like this guy pretty easy with a quick google search…

4 Reasons You Should Use JSON Instead of CSV

So do you really want me to rehash it here? Let’s just say – I use JSON because it’s cleaner to READ with Notepad. And yes.. I still use notepad. Every. Day.

# Output an array/object to JSON

$OutputJson = ConvertTo-Json -InputObject $OBJ_Group

$OutputJson | Out-File e:\lists\Json.txt

# Input an Json Text file:

$Input_Content = Get-Content “e:\lists\Json.txt”

#convert to an array:

$InputJsonArray = ConvertFrom-Json -InputObject $Input_Content


Powershell – More Code Snippits

Getting the Date:

$now = Get-Date

I like this format for File names:

$Cdate = get-date -f “yyyy-MM-dd-HH-mm-ss”

How to log to a file:

#
Log to file
#
$STR_Error = $null
$NOW = Get-Date -Format s
$STR_Error = $NOW + “, ” + $STR_AffectedGroupName + “, FAILED, AD Group not found.,” + $STR_ADGETERROR
Echo $STR_Error | Out-File $log -Append
$STR_Error = $null