Automation of Monthly Update Recompose/Push Image Process for Clone Pools. Part 2 of 4 of the: DevOps meets VMware Horizon. Automating the daily busy tasks out of the way series.

This is script is the culmination of many hours of work at night and on the weekends. This has been a personal goal of mine was to automate as much of the daily job that I can. And this is where I chose to start. This will allow me to start becoming Proactive instead of reactive.

So here I go! I give you the ability to do full monthly or weekly or daily update cycles of your clone pools from start to finish 100% scripted. Yes, you are reading that right. With this you are able to do software installs based on SCCM and/or Windows updates, Do 3rd Party patching, shut down the VM take a snapshot, clean out the old snaps, power it back on and prepare for the process to start next month. Yes, that sounds like a ton of stuff and it really is.

Time to get down to the features. What is does this script do? Well, here you go:

  1. Captures Service accounts and passwords for vCenter, Horizon
  2. Connects to the vCenters and get a list of the master machines based on a vCenter folder name.
  3. Builds out Installation and Shutdown script on a Share location that distributes the scripts to the VDI Master images.
  4. Runs the Install Updates function. This is the heart of the script laid out in a previous post HERE.
    • Runs a report of what the current installed 3rd party software is before updating
    • Install updates from SCCM
    • Install 3rd Party from SCCM (Requires Automatic install of Software)
    • Install Windows updates (If not managed by SCCM)
    • Install 3rd Party updates (If not managed by SCCM, and requires you to find appropriate scripts to install updates. There are many things on GitHub for the installs.)
    • Run a report of what the current installed 3rd party software is after updating.
    • Run a report of what the installed updates are since the last recompose
    • Create a custom registry key to store the last recompose update.
  5. Upon reboot of the master VM, it will run the cleanup script from a previous post HERE. This will clean up the master and make it ready to be cloned.
    • Checks the version of windows. (This is used for the optimizer at the end.)
    • Runs Disk Cleanup
    • Runs Defragment
    • Pre-Compile .NET Framework
    • Runs SEP update, Scan, Forced Check-in, and Clone Prep.
    • Runs VMware Optimizer based on what OS version you are on and what templates you have defined. (Beings its ran via CMD there are no rolling back changes, it’s a bug/Feature in the Optimizer)
    • Disables or Stops Services like Windows Updates, SCCM, Adaptiva. AppVolumes
    • Clean out Downloads Cache folder
    • Clean out Windows Prefetch
    • Clear the event logs
    • Releases IP Addresses
    • Clears DNS
    • Shutdown the VM
  6. Create a snapshot of each of the Masters in there Powered Off State.
  7. Update the VM Notes in vCenter to show Last Recomposed: and the date.
  8. Will remove the old snapshots past X number of days old.
  9. Will Power the Master VMs back on.
  10. Start the SCCM and Windows Update services.
  11. Copy’s the log files from each VDI Master back to the central share.
  12. It will connect to the Horizon View Connection servers.
  13. Recompose or do image Push to the Pools, Varying the times based on if they are Production or Test.

 

There a few requirements for this script. 

  • Must be running Powershell v5.1 or newer on the Scripting Server and the Master VMs
  • The account you are running the script as has to have admin rights to the Master Images, Rights to do recompose or image push in Horizon, and appropriate rights in vCenter to take Snapshot, delete snapshots, and write notes.
  • Must have a folder share setup with the Read and Write rights to the user you will run the script as. I set up the following directories like this.
    • \\ShareLocation\VDI_Tools
      • Logs (This is where all the log files will be saved)
      • Scripts (Save this script and run it from this location)
      • CloneTools (Place your VMware Optimizer and templates in that location.
  • The VMware Modules must be installed on the Scripting Server. (Out of laziness I just enable them all.)
  • Must install the HV-Helper module on the Scripting Server. The code can be found HERE.
      1. Click the green Clone or download button and then click Download ZIP.
      2. Extract the zip file and copy the advanced functions Hv.Helper folder to a modules directory.
      3. Check your PowerShell $env: PSModulePath variable to see which directories are in use:
        • User-specific:  %UserProfile%\Documents\WindowsPowerShell\Modules
        • Systemwide: C:\Program Files\WindowsPowerShell\Modules
      4. Unblock the advanced functions to allow them to be executed.
        • In a PowerShell prompt (as Administrator), run the following command, tailoring the path to where you copied the VMware.Hv.Helper folder:
      dir ‘C:\Program Files\WindowsPowerShell\Modules\VMware.HvHelper\’ | Unblock-File
  • This only works on Horizon 7.0.2 and newer deployments! So if you are not there yet get to updating.
  • If you want to run VMware OS Optimization Tool Fling you must have it downloaded and installed in the “CloneTools” share directory, with your custom Templates. For example, this is my configuration. OptimizeFolder
  • All VDI Master Images must be domain joined. It uses domain authentication to do the remote PowerShell.
  • Enable-PSRemoting must be enabled on All the VDI Master Images.

There are a couple known bugs:

  1. One is running this more than one time a day will create two snapshots with the same name, and then try to recompose or push the image and get confused as there are two snaps of the same name. So it will fail to update the recompose or push part fo the script. Workaround: Remove the previous snaps for the same day before running the script again.
  2. Running the script late in the day, like the last hour of the day. When it runs the script its date dependent. If the script is kicked off on the 12-4-2018 there is a possibility that the snaps might be taken on 12-5-2018, and when the recompose or Push happens it will be looking for a snap from 12-4-2018. In turn, erroring out and failing the Recompose or Push for those pools. Workaround: Run the script early in the day or morning to avoid running into the next day. On at minimum, the script takes 17 mins to run Per Pool. Please plan accordingly.Known Issues
  3. If the credentials that you use to run the script have expiring passwords it will error out and fail the script and not prompt for new creds. (Might be an added feature soon.) Workaround: Use service account with a non-expiring password or delete the password file from the Script Server when you reset your password.
  4. The script uses vCenter Folders to do the updates to your VDI Master Image VMs. But there may be a possibility that the VMs in that “Masters” folder may be more than just Master images. If so it will update those VMs also. Also if your Master’s folder has different VMs than what you have for master images in your Horizon environment it might error on the Recompose or Push parts. Workaround: Make sure the VMs in the “Masters” folder in vCenter are your Master Clone images in Horizon. (I hope to swap out the folder and just use horizon list. Was a design flaw as this was originally built to support me, and I knew what was in my folders.)
  5. Multi-Domain issues. If your scripting server is in a different domain than your VDI master images you will run into some DNS lookup issues. As in the script, we are not using FQDN to connect to the master Images we are just using hostname. Sorry if this causes an issue. Workaround: Can be fixed pretty simple by adding a domain variable and appending most of the $VMline variables in the script. (Can not promise when I get to fixing this one.)

 

Here are a few of before and after Shots of what is going on.

Snapshots:

Manage Snapshots Before

Snapshots Before

Manage SnapshotsAfter

Snapshots After

 

vCenter Notes:

vCenter Before

Before Script has run

vCenterAfter

After Script has run

Pool Details:

HorizonConsolePoolBefore

Pool Before

HorizonConsolePoolAfter

Pool After

Image Push:

HorizonConsoleMachines

Pending Push

HorizonConsoleMachinesAfterPush

Completed Push

Task List:

vCenterTasksAfter

Custom Registry Entry:

MasterVMRegistryKey

Log files in the Share:

LogFilesShare

Installed Software Log Example:

Transcript Log

VMware Optimizer Report:

VMware Optimization Report

Transaction Log Report:

Transcript Log

 

The Code! This has been a long process and a ton of trial and error to get all this to work.

Link to the code on GitHub!

I will continue to update and optimize this code so check back frequently. If There are any issues found please let me know. There are a ton of features in the works I just figured it was best to get a working reliable version out there prior to monkeying with it more.

 

What is in the works for this script you ask?

  • Breaking the Script into Modules so allow better plug and play and customizations.
  • ServiceNow integrations (open incident, Open change under existing incident, Post log of the changes that were made. Close the change, and then close the incident)
  • If no ServiceNow building out email functions for reporting. (If someone wants to devote the time to a good HTML body let me know. I just planned on plain text.
  • Add Multi-Threading (I have run into some issues trying to deploy in this version and have held until I had more time to test.)
  • Replace using vCenter Folders for using horizon to query for Master VMs names and the vCenter names.

Last but not least. A huge thank you to Michael McDonnell for the help. Your help and guidance and mentorship have been much appreciated. And thanks to the MANY blog posts of other people and the mass amount of TechNet and PowerShell docs I have read of the last few months. And big thanks to Wouter Kursten for creating the HV-Helper, and answering some of my odd questions!

Advertisements
Posted in CLI and Powershell, DevOps, Service Now, VDI, Virtulization | Tagged , , , , , , , , , , | Leave a comment

Building Service Now Functions for adding to existing automation scripts.

I have been working on some bigger automation projects here lately. And ran into the need to learn more about Service Now and how to create Incidents and Change Requests via PowerShell and also how to add them to the Scripts I was already working on. So in good fashion, there was not much out there already so here is my rendition of what I needed.

This script is taking what I already did in the last 3 Service Now posts and creatingSNDev functions. This allowing for quick plug and play into the scripts going forward. Just in case you have not read them here are the previous posts that give some background on where this all came from.

Service Now Incident Creation and Updating with PowerShell

Service Now Change Request Creation and Updating with PowerShell

Using the ServiceNow Rest API Explorer

This script is broken into 3 main functions. Create, Update and Get. For my use case, I was creating an Incident and then creating a Change Request with the Incident as the parent.

# Build Service Now Functions for Create, Update, and Get Incidents and Changes

# Chris Hildebrandt

# 11-25-2018

# Ver 1.0

# Script will create a new ServiceNow Incident and Change, Update existing Incidents and Changes, and Get all details of Incidents and Changes via Powershell

#______________________________________________________________________________________

#______________________________________________________________________________________

#ServiceNow Varibles

$SNAddress = "https://YOURSERVICENOW.service-now.com"

#______________________________________________________________________________________

#Incident Varribles

$SNINCCallerID = "Caller Sys_ID"

$SNINCUrgency = "2" #Look this up in your Service Now instance

$SNINCImpact = "3" #Look this up in your Service Now instance

$SNINCPriority = "4" #Look this up in your Service Now instance

$SNINCContactType = "email" #Look this up in your Service Now instance

$SNINCNotify = "2" #Look this up in your Service Now instance

$SNINCWatchlist = "Watch List Sys_ID" #Can do comma seperated users Sys_ID's

$SNINCServiceOffering = "Service Offering" #Look this up in your Service Now instance

$SNINCProductionImpact = "No" #Well I hope its a No.

$SNINCCategory = "Your Catagory" #Look this up in your Service Now instance

$SNINCSubcategory = "Your SubCat" #Look this up in your Service Now instance

$SNINCItem = "request" #Look this up in your Item menu

$SNINCAssignmentGroup = "Assignment Group Sys_ID"

$SNINCAssignedTo = "Assigned To Sys_ID"

$SNINCShortDescription = "Short Discription"

$SNINCDescription = "Full Discription"

$SNINCWorkNotes = "Work Notes"

$SNINComments = "Notes"

#______________________________________________________________________________________

#Change Varribles

$SNCHGRequestedBy = "Requested By Sys_ID"

$SNCHGCategory = "Change Catagory" #Look this up in your Service Now instance

$SNCHGServiceOffering = "Service Offering" #Look this up in your Service Now instance

$SNCHGReason = "Change Reason" #Look this up in your Service Now instance

$SNCHGClientImpact = "No" #Look this up in your Service Now instance

$SNCHGStartDate = (Get-Date).ToString('yyyy-MM-dd HH:mm:ss') #date in string format. Only way it works.

$SNCHGEndDate = (Get-Date).AddHours($RCTimeDelay2+24).ToString('yyyy-MM-dd HH:mm:ss') #24 hour delay of right now (Can Change if needed) Has to be as a string

$SNCHGWatchList = "Watch List Sys_ID" #Can do comma seperated users Sys_ID's

$SNCHGUrgency = "2" #Look this up in your Service Now instance

$SNCHGRisk = "4" #Look this up in your Service Now instance

$SNCHGType = "Standard" #Look this up in your Service Now instance

$SNCHGState = "1" #Look this up in your Service Now instance

$SNCHGAssignmentGroup = "Assignment Group Sys_ID"

$SNCHGAssignedTo = "Assigned To Sys_ID"

$SNCHGShortDescription = "Short Description"

$SNCHGDescription = "Description Test"

$SNCHGJustification = "Justification Notes"

$SNCHGChangePlan = "Change Plan"

$SNCHGTestPlan = "Test Plan Notes"

$SNCHGBackoutPlan = "Back Out Plan Notes"

$SNCHGChangeSummary = "Change Summary Notes"

#______________________________________________________________________________________

#Create Service Now Creds if they do not already exist

if(-Not (Test-Path -Path "C:\VDI_Tools\Scripts\SNAccount.txt" ))

{

Get-Credential-Message "Enter Your ServiceNow Account! Username@domain"|Export-Clixml"C:\VDI_Tools\Scripts\SNAccount.txt"

Write-Host"Created Secure Credentials for ServiceNow"

}

#______________________________________________________________________________________

#Import Service Now Creds

$SNCreds = Import-Clixml "C:\VDI_Tools\Scripts\SNAccount.txt"

Write-Host "Imported Secure Credentials for ServiceNow from Text file"

#______________________________________________________________________________________

#Decrypt Password to imput into ServiceNow

$SNTextPass = $SNCreds.Password | ConvertFrom-SecureString

$SNTextPassPlain = [Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR( (ConvertTo-SecureString $SNTextPass) ))

Write-host "Decrypt Master Password to clear text for import into VM"

#______________________________________________________________________________________

#Create ServiceNow Creds Varribles

$SNuser = $SNCreds.Username

$SNpass = $SNTextPassPlain

#______________________________________________________________________________________

#ServiceNow Method Varibles Do not edit these

$SNMethodPost = "post"

$SNMethodGet = "get"

$SNMethodPut = "put"

$SNMethodPatch = "patch"

$SNINCAddress = "$SNAddress/api/now/table/incident"

$SNCHGAddress = "$SNAddress/api/now/table/change_request"

#______________________________________________________________________________________

#ServiceNow Build Auth Headers

$base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f $SNuser, $SNpass)))

#______________________________________________________________________________________

#ServiceNow Set Header

$SNheaders = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"

$SNheaders.Add('Authorization',('Basic {0}' -f $base64AuthInfo))

$SNheaders.Add('Accept','application/json')

$SNheaders.Add('Content-Type','application/json')

Function Create-Incident()

{

# Specify request body

$SNCreateINCBody = @{ #Create Body of the Post Request

caller_id=$SNINCCallerID

urgency=$SNINCUrgency

impact=$SNINCImpact

priority=$SNINCPriority

contact_type=$SNINCContactType

notify=$SNINCNotify

watch_list=$SNINCWatchlist

service_offering=$SNINCServiceOffering

u_production_impact=$SNINCProductionImpact

category=$SNINCCategory

subcategory=$SNINCSubcategory

u_item=$SNINCItem

assignment_group=$SNINCAssignmentGroup

assigned_to=$SNINCAssignedTo

short_description=$SNINCShortDescription

description=$SNINCDescription

work_notes=$SNINCWorkNotes

comments=$SNINComments

}

$SNCreateINCbodyjson = $SNCreateINCBody | ConvertTo-Json

# POST to API

Try

{

# Send API request

$SNCreateIncResponse = Invoke-RestMethod -Method $SNMethodPost -Uri $SNINCAddress -Body $SNCreateINCbodyjson -TimeoutSec 100 -Headers $SNheaders -ContentType "application/json"

}

Catch

{

Write-Host $_.Exception.ToString()

$error[0] | Format-List -Force

}

return $SNCreateIncResponse

}

Function Create-Change()

{

#Specify Change Request Body

$SNCreateCHGbody = @{ #Create Body of the Post Request

requested_by=$SNCHGRequestedBy

category=$SNCHGCategory

service_offering=$SNCHGServiceOffering

reason=$SNCHGReason

u_client_impact=$SNCHGClientImpact

start_date=$SNCHGStartDate

end_date=$SNCHGEndDate

watch_list=$SNCHGWatchList

parent=$SNIncidentSysID

urgency=$SNCHGUrgency

risk=$SNCHGRisk

type=$SNCHGType

state=$SNCHGState

assignment_group=$SNCHGAssignmentGroup

assigned_to=$SNCHGAssignedTo

short_description=$SNCHGShortDescription

description=$SNCHGDescription

justification=$SNCHGJustification

change_plan=$SNCHGChangePlan

test_plan=$SNCHGTestPlan

backout_plan=$SNCHGBackoutPlan

u_change_summary=$SNCHGChangeSummary

}

$SNCreateCHGbodyjson = $SNCreateCHGbody | ConvertTo-Json

# POST to API

Try

{

# Send API request

$SNCreateChangeResponse = Invoke-RestMethod -Method $SNMethodPost -Uri $SNCHGAddress -Body $SNCreateCHGbodyjson -TimeoutSec 100 -Headers $SNheaders -ContentType "application/json"

}

Catch

{

Write-Host $_.Exception.ToString()

$error[0] | Format-List -Force

}

return $SNCreateChangeResponse

}

Function Update-Change($SNCHGUpdateComments)

{

# Specify request body

$SNUpdateCHGbody = @{ #Create Body of the Post Request

comments=$SNCHGUpdateComments

}

$SNUpdateCHGbodyjson = $SNUpdateCHGbody | ConvertTo-Json

# Send API request

$SNUpdateChangeResponse = Invoke-RestMethod -Method $SNMethodPatch -Uri "$SNCHGAddress\$SNChangeSysID" -Body $SNUpdateCHGbodyjson -TimeoutSec 100 -Headers $SNheaders -ContentType "application/json"

}

Function Update-Incident($SNINCUpdateWorkNotesUpdate)

{

# Specify request body

$SNUpdateINCbody = @{ #Create Body of the Post Request

work_notes=$SNINCUpdateWorkNotesUpdate

}

$SNUpdateINCbodyjson = $SNUpdateINCbody | ConvertTo-Json

# Send API request

$SNUpdateIncidentResponse = Invoke-RestMethod -Method $SNMethodPatch -Uri "$SNINCAddress\$SNIncidentSysID" -Body $SNUpdateINCbodyjson -TimeoutSec 100 -Headers $SNheaders -ContentType "application/json"

}

Function Get-Incident($SNGetIncidentSysID)

{

# Build URI

$SNGetINCAddress = "$SNINCAddress/$SNGetIncidentSysID" + "?sysparm_fields=parent%2Ccaused_by%2Cwatch_list%2Cu_aging_category%2Cu_call_back_number%2Cupon_reject%2Csys_updated_on%2Cu_resolved_by_tier_1%2Cu_ud_parent%2Cu_resolved_within_1_hour%2Cu_routing_rule%2Capproval_history%2Cskills%2Cu_actual_resolution_date%2Cnumber%2Cu_related_incidents%2Cu_closure_category%2Cstate%2Csys_created_by%2Cknowledge%2Corder%2Cdelivery_plan%2Ccmdb_ci%2Cimpact%2Cu_requested_for%2Cactive%2Cpriority%2Cgroup_list%2Cbusiness_duration%2Cu_template%2Capproval_set%2Cwf_activity%2Cu_requested_by_phone%2Cshort_description%2Cu_itil_watch_list%2Cdelivery_task%2Ccorrelation_display%2Cwork_start%2Cu_ca_reference%2Cadditional_assignee_list%2Cnotify%2Cservice_offering%2Csys_class_name%2Cfollow_up%2Cclosed_by%2Creopened_by%2Cu_csv_comments%2Cu_planned_response_date%2Creassignment_count%2Cassigned_to%2Csla_due%2Cu_actual_response_date%2Cu_sla_met%2Cu_closure_ci%2Cu_reopen_count%2Cescalation%2Cupon_approval%2Cu_service_category%2Ccorrelation_id%2Cu_resolution_duration%2Cu_requested_by_name%2Cmade_sla%2Cu_requested_by_email%2Cu_item%2Cu_svc_desk_created%2Cresolved_by%2Cu_business_service%2Csys_updated_by%2Cuser_input%2Copened_by%2Csys_created_on%2Csys_domain%2Cu_quality_impact%2Cu_req_count%2Ccalendar_stc%2Cclosed_at%2Cu_relationship%2Cu_parent_incident%2Cu_comments_and_work_notes%2Cu_requested_by_not_found%2Cu_requested_by%2Cbusiness_service%2Cu_agile_incident_ref%2Cu_symptom%2Crfc%2Ctime_worked%2Cexpected_start%2Copened_at%2Cwork_end%2Creopened_time%2Cresolved_at%2Ccaller_id%2Cu_client%2Cwork_notes%2Csubcategory%2Cu_ah_incident%2Cclose_code%2Cassignment_group%2Cbusiness_stc%2Cdescription%2Cu_planned_resolved_date%2Ccalendar_duration%2Cu_on_hold_type%2Cu_source%2Cclose_notes%2Cu_closure_subcategory%2Cu_previous_assignment%2Csys_id%2Ccontact_type%2Curgency%2Cproblem_id%2Cu_itil_group_list%2Cu_response_duration%2Cu_best_number%2Ccompany%2Cactivity_due%2Cseverity%2Cu_production_impact%2Ccomments%2Capproval%2Cdue_date%2Csys_mod_count%2Csys_tags%2Clocation%2Ccategory"

# Specify request body

$SNGetINCbody = @{

}

$SNGetINCbodyjson = $SNGetINCbody | ConvertTo-Json

# Send API request

$SNGetIncidentResponse = Invoke-RestMethod -Method $SNMethodGet -Headers $SNHeaders -Uri $SNGetINCAddress

Return $SNGetIncidentResponse

}

Function Get-Change($SNGetChangeSysID)

{

To run the code you can do something like this…

$SNCreateIncResponseReturn = Create-Incident

$SNIncidentID = $SNCreateIncResponseReturn.result.number
$SNIncidentSysID = $SNCreateIncResponseReturn.result.sys_id

Write-Host $SNIncidentSysID
Write-Host $SNIncidentID

Above will create an Incident and then populate the IncidentID and SysID variables. That gives you your INC number and your Sys_ID of the incident.

You can do the same thing with the change. Keep in mind that my code uses the IncidentSysID to create a change with the parent of the Incident we just created.

$SNCreateChangeResponseReturn = Create-Change

$SNChangeID = $SNCreateChangeResponseReturn.result.number
$SNChangeSysID = $SNCreateChangeResponseReturn.result.sys_id

Write-Host $SNChangeSysID
Write-Host $SNChangeID

Same as above this will create the change and give you your Change Number and Sys_ID number.

You can update Incident you created running just the simple command of:

update-incident "Update the change Notes"

Or if you want to import a file contents you can run the following command.

update-incidents $content = [IO.File]::ReadAllText("File Path and name")

This will read all the contents of the file and post it into the Incident Comments. Great when you uploading logs.

Can do the same thing with the change.

update-change "Update Change Work Notes"

or the import file contents

update-incidents $content = [IO.File]::ReadAllText("File Path and name")

Next is the Get Incident and Change. For me, I wanted to save all the change and incident info in a log file for reference later if I ever had an issue.

Get-Incident $SNIncidentSysID

This will write on the console all the contents of the Incident. or you can run the same thing for the Change Request

Get-Change $SNChangeSysID

There are a massive amount of other things you can do with the Service Now API explorer. Maybe as I have time I will dig more into it.

All the contents to this script are posted on my GitHub repo HERE. Along with my other Service Now stuff from the other blogs.

Posted in CLI and Powershell, Random Crap, Service Now | Tagged , , | Leave a comment

Service Now Change Request Creation and Updating with PowerShell

I finally am getting around to posing the addition to my previous blog post for Creating and updating Service Now Incidents.

SNDevSame as before you need to know your Sys_ID’s of the objects you are going to use. Like your user account, The assignment group and anyone else you are going to need to add to this change request.

Finding Sys_IDs is easy once you figure out they are a 32 character code listed in the URL of the objects you are looking for. Same with your Incident’s.

First Open an existing incident with your name in it. On the right of your name is the “i” Click on it and then the “Open Record” button on the upper right-hand side. ChrisHil

This will open your User record in ServiceNow. In the URL you will see your Sys_ID. ChrisHSys_ID

You can do this for the rest of the other Sys_ID’s you need. Also in this change, I have the parent filed in the script, So if you find the Sys_ID of the parent Incident you can put that as the parent for the change.

From there you just need to gather all the info that is required or you would like to use.

# Create New ServiceNow Change

# Chris Hildebrandt

# 10-26-2018

# Ver 1.0

# Script will create a new ServiceNow Change via Powershell

#______________________________________________________________________________________

# Eg. User name="admin", Password="admin" for this code sample.

$SNuser = "admin" #enter your ServiceNow Username

$SNpass = "admin" #enter your ServiceNow Password

# Build auth header

$base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f $SNuser, $SNpass)))

# Set proper headers

$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"

$headers.Add('Authorization',('Basic {0}' -f $base64AuthInfo))

$headers.Add('Accept','application/json')

$headers.Add('Content-Type','application/json')

# Specify endpoint uri

$CHGuri = "https://YOURSERVICENOW.service-now.com/api/now/table/change_request"

# Specify HTTP method

$Postmethod = "post"

# Specify request body

$CHGbody = @{ #Create Body of the Post Request

requested_by = "Requested By Sys_ID"

category = "Other"

service_offering = "Other"

reason = "software upgrade"

u_client_impact = "No"

start_date = "2018-11-1 01:00:00"

end_date = "2018-11-30 23:00:00"

watch_list = "Watch List Sys_ID"

parent = "Parent Incident or Change Request"

urgency = "2"

risk = "4"

type = "Standard"

state = "1"

assignment_group = "Assigned To's Group Sys_ID"

assigned_to = "Assigned To Sys_ID"

short_description = "Short Description"

description = "Description: Test"

justification = "Justification Notes"

change_plan = "Change Plan:"

test_plan = "Test Plan: Notes"

backout_plan = "Back Out Plan: Notes"

u_change_summary = "Change Summary: Notes"

}

$CHGbodyjson = $CHGbody | ConvertTo-Json

# POST to API

Try

{

# Send API request

$ChangePOSTResponse = Invoke-RestMethod -Method $Postmethod -Uri $CHGuri -Body $CHGbodyjson -TimeoutSec 100 -Headers $headers -ContentType "application/json"

}

Catch

{

Write-Host $_.Exception.ToString()

$error[0] | Format-List -Force

}

# Pulling ticket ID from response

$ChangeID = $ChangePOSTResponse.result.number

$ChangeSysID = $ChangePOSTResponse.result.sys_id

# Verifying Change created and show ID

IF ($ChangeID -ne $null)

{

"Created Change With ID:$ChangeID"

"Change created With Sys_ID:$ChangeSysID"

}

ELSE

{

"Change Not Created"

}

The script below is an extremely basic script that I have built for my use case but this has huge building blocks from here. In this script, you need to edit the username and password. You can use encrypted if you like. You will need your ServiceNow URL and of course all your Sys_ID’s and info from the fields that have drop-down menus. Later on, in a blog post, I will put something out there on a script I created making all the Service Now code into functions to use in much larger scripts.

 

This script below will update an existing Service Now Change. You will need the Sys_ID for the Change you are updating. That can be found in the URL of the change.

Sys_ID

# Update New ServiceNow Change

# Chris Hildebrandt

# 10-26-2018

# Ver 1.0

# Script will Update a ServiceNow Change via Powershell

#______________________________________________________________________________________

# Eg. User name="admin", Password="admin" for this code sample.

$SNuser = "admin" #enter your ServiceNow Username

$SNpass = "admin" #enter your ServiceNow Password

# Build auth header

$base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f $SNuser, $SNpass)))

# Set proper headers

$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"

$headers.Add('Authorization',('Basic {0}' -f $base64AuthInfo))

$headers.Add('Accept','application/json')

$headers.Add('Content-Type','application/json')

# Specify endpoint uri

$UpdateCHGuri = "https://YOURSERVICENOW.service-now.com/api/now/table/change_request/THE Change you want to updates Sys_ID"

# Specify HTTP method

$Patchmethod = "patch"

# Specify request body

$UpdateCHGbody = @{ #Create Body of the Post Request

state="8"

comments="Additional Coments "#Add the comments you want to add to the Change.

u_change_summary="Change Summary"#Add to the Change Summary.

}

$UpdateCHGbodyjson = $UpdateCHGbody | ConvertTo-Json

# Send HTTP request

$response = Invoke-WebRequest -Headers $headers -Method $method -Uri $uri -Body $body

# Send API request

$UpdateChangePOSTResponse = Invoke-RestMethod -Method $Patchmethod -Uri $UpdateCHGuri -Body $UpdateCHGbodyjson -TimeoutSec 100 -Headers $headers -ContentType "application/json"

I was working with Comments and Change Summary you can add other fields as you like.

The Links for these Scripts are on my GitHub repo HERE. Along with a few other Scripts.

Posted in CLI and Powershell, DevOps, Service Now | Tagged , , | Leave a comment

Using the ServiceNow Rest API Explorer

A month or so ago I was introduced into the world of the ServiceNow Rest API explorer. It was a rough into at best.

A little backstory always helps. I am still working on a project to automate most of my daily tasks in the VDI world. The need to create Incidents and Change tickets as part of the process I needed. I reached out to the ServiceNow team at work and asked if he had any automation scripts built, if not could I have access to the API Explorer. And the email responding back to this request was “No I do not have any automation built for SNDevServiceNow, If you would like to use it here is our ServiceNow Dev site URL, and Here are some credentials to log in.” As a person with limited RestAPI skills, this was kind of a dreaded moment. So I pick a weekend and jump in head first with the goal of wanting to be able to create and Update both Incidents and Changes by the end of the weekend.

If you google around you don’t find much out there for building scripts for this. And after the fact, I realize why. I don’t think anyone fully understands the API. Some may laugh at this but as an Infrastructure and VDI guy, this is a different concept for me. I have done a ton of work in PowerShell but limited with the API world so looking at this is more like reading Chinese.

So step One was finding the API Explorer…..This is not on the normal menu on the left side so of course, you have to search for it at the top. The next part is understanding what the API explorer does and how it can help you. Below is what you walk into. API Exp1

Retrieve records from a table  (GET):
Create a record  (POST)
Retrieve a record  (GET)
Modify a record  (PUT)
Delete a record  (DELETE)
Update a record  (PATCH)

These 6 API calls are extremely useful. I have not spent much time with “Get Table” or Delete but getting pretty comfortable with the Post, Get, Put.

 

When I started working with this I had no idea on where to start. I tried to use the Post function first to see what the fields are. But it was rather confusing to me as I could not get it to fill in the caller info and a few other fields that required the Sys_ID. Decided to give up on this as I had no idea on how to use it. I decided to take a different route and went with creating an Incident from the normal create an Incident function from the web GUI. I filled out the Incident with all the fields with all the data I wanted to use, to begin with, so I can backward engineer from there. After creating the incident I started the process of doing a “Retrieve a Record”. Get Record Inc

After clicking on Retrieve a record  (GET) on tableName find Incident. Then put in your Sys_ID. The Sys_ID is in the URL for the Incident. Sys_ID

The Sys_ID is a 32 Character ID that is located in the URL of the objects you are looking up. As you see above this one is for our Incident. Now that you have this info in, you need to scroll down a bit to the sysparm_fields. This is where you choose what info to get back from your Incident. Fileds to show

By default noting is in the Selected Colum out of lack of wanting to dig through the mess on the left I just normally highlight all and add them all. Then you don’t miss any of the fields that you may or may not want.

I leave the Formats in JSON. and then for this, I click the send button. But if you notice below the Send button in the Code Samples below. These are code samples are exactly what you just put in the fields above to retrieve the same info as you requested with the Send button. You can copy and paste the code and use it in your script. For example, this is the Powershell button.

Get INC PowerShell

But further this if you scroll past the send button and the code examples it will show you when the Send button did, and if it was successful. It will show the HTTP method call, you can just copy and paste that URL into a browser and get the data. Also, it shows that the Status of the code was 200 meaning all good. It would give you a 404 error and be in red if you had the wrong Sys_ID in there.

Get HTTP and junk

Going beyond that it will show you the best part. It will show you all your results from your query. Yes all those fields with what service now calls those fields and how they populate them. For me, I just copied the data and pasted it into a Notepad so I could reference it in building my Create Incident script. Response Body

From here there was more of an understanding of what fields are named and what data is expected in the area. And yes that long 32 character code is the Sys_ID for my user. But there are many more referenced in the ticket depending on what fields you populated. From here I felt confident on how to work my way through how to create an incident. After all, I had all the data I wanted to put in there. So go back to the top and choose to Create a record  (POST). Then select the table for Incident again.CreateInc Table

I just left the fields all default as all the data I cared about was below this. Looking below you see the Request Body area. This is where all the magic happens. If you click on “Add a Field” you a row of data you will fill.

Request Body

If you click on “Add a Field” you a row of data you will fill. When you do that you also see what is required in the data field. For example, “Caller” is looking for the Sys_ID code for the person calling this Incident. And Urgency is asking for an Integer, and “Close notes” is looking for a String. I also filled in “Assigned To” to show how it fills in the data below.

Request Body Filled

Once you have entered all your data you can scroll down to the bottom of the page. If you click the Send button it will create an incident with all the data you just put in and show you it all works or does not. But click on the Code Examples that you want to use. For me, it was PowerShell. Click on it and it will give you the code to do what you need to do.

Inc PowerShell Code Example

Here you can just copy all the code, paste it into an ISE window and run it. WRONG. you need to put in your creds is one thing. But there is a hidden JEM of an issue from the raw code. You will the below crap of missing a closing }. I tried to correct it and find a suitable place, but gave up. After some Google-Fu, I found an easy workaround was to just rebuild the Body variable into an array.

Powershell INC Error

Building an array was a pretty easy workaround and also made the code look cleaner. No longer was your body statement one really long line it was now going to be a list. For me, I commented out the existing body line and just started to create my new one below it so I could copy the data I needed from the line.

# Specify request body
$body = @{ #Create Body of the Post Request
caller_id= "29711cc64f2bd308feda99701310c727"
urgency= "2"
impact= "3"
priority= "4"
category= "Your Catagory Find from the drop down menu"
subcategory= "Find the subcategory from the list"
u_item= "request" #Check the Drop down for the options
short_description= "Short Description of the Incident"
description= "Description of the Incident"
work_notes= "Work Notes"
comments= "Additional Comments and Notes"

}
$bodyjson = $body | ConvertTo-Json

# Send HTTP request
$CreateServiceIncident = Invoke-WebRequest -Headers $headers -Method $method -Uri $uri -Body $bodyjson -ContentType "application/json"

With replacing the body of the script now all should work well for you. Make sure you do change the last part of the HTTP request to replace the existing body with

-Body $bodyjson -ContentType “application/json”

This will simplify the issues. Now you should be able to automate your ServiceNow incidents.

In conclusion, you should be able to apply this to the creation of Change tickets and much more. I will try to continue to post on this topic as it seems there Is a lot of lost info in this area. I do have some prebuilt Scripts for Creating an Incident and Updating an Incident located here.

 

Posted in Blogtober, CLI and Powershell, DevOps, Random Crap, Service Now | Tagged , , ,

Service Now Incident Creation and Updating with PowerShell

For those of you that are cursed or blessed by the use of ServiceNow, I have been working on a project to automate a ton of my day to day job. Part of this automation of the service Incidents. I spent a few painful hours messing around with the ServiceNow rest API’s. Keep in mind I am no developer and don’t know Rest but after enough trial SNDevand error, I managed to get something out of it. I thought it would be worth the effort to save you all some pain and anguish.

There are a few things you will need to make this productive. You will need to know the Sys_ID of your ServiceNow account, the Sys_ID of the Assignment Group and Service Offering. Also, remember that this also depends on your Incident Form and what your company has customized. For us, we have a few required fields so I had to make sure those were filled.

Finding Sys_IDs is easy once you figure out they are a 32 character code listed in the URL of the objects you are looking for. Same with your Incident’s.

First Open an existing incident with your name in it. On the right of your name is the “i” Click on it and then the “Open Record” button on the upper right-hand side. ChrisHil

This will open your User record in ServiceNow. In the URL you will see your Sys_ID. ChrisHSys_ID

You can do this for the rest of the other Sys_ID’s you need.

From there you just need to gather all the info that is required or you would like to use.

This is a basic script to fill out all the fields I needed for my use case. In this script, you need to edit the username and password. You can use encrypted if you like. You will need your ServiceNow URL and of course all your Sys_ID’s and info from the fields that have drop-down menus.

# Create New ServiceNow Incident
# Chris Hildebrandt
# 10-26-2018
# Ver 1.0
# Script will create a new ServiceNow Incident via Powershell
#______________________________________________________________________________________

#______________________________________________________________________________________
#ServiceNow creds
$user = "admin"
$pass = "admin"

# Build auth header
$base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f $user, $pass)))

# Set proper headers
$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
$headers.Add('Authorization',('Basic {0}' -f $base64AuthInfo))
$headers.Add('Accept','application/json')
$headers.Add('Content-Type','application/json')

# Specify endpoint uri
$uri = "https://YOURSERVICENOW.service-now.com/api/now/table/incident"

# Specify HTTP method
$method = "post"

# Specify request body
$body = @{ #Create Body of the Post Request
    caller_id= "29711cc64f2bd308feda99701310c727"
    urgency= "2"
    impact= "3"
    priority= "4"
    contact_type= "email"
    notify= "2"
    watch_list= "29711cc64f2bd308feda99701310c727"
    service_offering= "Service Offering 32 Char Sys_ID"
    u_production_impact= "No"
    category= "Your Catagory Find from the drop down menu"
    subcategory= "Find the subcategory from the list"
    u_item= "request"   #Check the Drop down for the options
    assignment_group= "Assignment Group 32 Char Sys_ID"
    assigned_to= "29711cc64f2bd308feda99701310c727"
    short_description= "Short Description of the Incident"
    description= "Description of the Incident"
    work_notes= "Work Notes"
    comments= "Additional Comments and Notes"

}
$bodyjson = $body | ConvertTo-Json

# Send HTTP request
$CreateServiceIncident = Invoke-WebRequest -Headers $headers -Method $method -Uri $uri -Body $bodyjson -ContentType "application/json"

# Print response
$CreateServiceIncident.RawContent

 

This script will update an existing ServiceNow Incident. You will need the Sys_ID for the incident you are updating. That can be found in the URL of the incident.

Sys_ID

# Update Existing ServiceNow Incident

# Chris Hildebrandt

# 10-26-2018

# Ver 1.0

# Script will update existing ServiceNow Incident via Powershell

#______________________________________________________________________________________

#______________________________________________________________________________________

#ServiceNow creds

$user = "admin"

$pass = "admin"

# Build auth header

$base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f $user, $pass)))

# Set proper headers

$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"

$headers.Add('Authorization',('Basic {0}' -f $base64AuthInfo))

$headers.Add('Accept','application/json')

$headers.Add('Content-Type','application/json')

# Specify endpoint uri

$uri = "https://YOURSERVICENOW.service-now.com/api/now/table/incident/YourINCIDENTSys_ID"

# Specify HTTP method

$method = "patch"

# Specify request body

$body = @{ #Create Body of the Post Request

work_notes="Update Work Notes"

close_notes="Your Close Notes"

}

$bodyjson = $body | ConvertTo-Json

# Send HTTP request

$response = Invoke-WebRequest -Headers $headers -Method $method -Uri $uri -Body $bodyjson -ContentType "application/json"

# Print response

$response.RawContent

 

This script you can add other fields if you like. For me, I was using Work Notes and Close Notes.

The Links for these Scripts are on my GitHub repo HERE. Along with a few other Scripts.

Posted in Blogtober, CLI and Powershell, DevOps, Service Now | Tagged , ,

Automation of the update process for Horizon Clone Master Images. Part 3 of 4 DevOps meets VMware Horizon. Automating the daily busy tasks out of the way.

Over the last few months, I have been working heavily on building a model of managing the process to patch for Horizon Linked and Instant Clone masters. After some long hard work, I have managed to create a process to install updates and 3rd party application updates.

Our process was built around using SCCM to install Windows updates to the master image, and also 3rd Party Software. After talking about the concept of automating the Recompose/Push process. I realized how many people could use this. So in the process, I decided to write the code to install Windows Updates from Update Manager, and 3rd party application from other PowerShell scripts.

The Script I created does the following:

  • Runs a report of what the current installed 3rd party software is before updating
  • Captures Credentials for VM Masters
  • Copy Files from Share to Master VM
  • Install updates from SCCM
  • Install 3rd Party from SCCM (Requires Automatic install of Software)
  • Install Windows updates (If not managed by SCCM)
  • Install 3rd Party updates (If not managed by SCCM, and requires you to find appropriate scripts to install updates. There are many things on GitHub for the installs.)
  • Setup an AutoLog On process to the Master VM. This will Log on to the VM after reboot and run the shutdown script LINK HERE.
  • Run a report of what the current installed 3rd party software is after updating.
  • Run a report of what the installed updates are since the last recompose
  • Create a custom registry key to store the last recompose update.

This script is highly dependent on the folder structure of the following.

  • C:\VDI_Tools
    • Logs (This is where all the log files will be saved)
    • Scripts (Save this script and run it from this location)
    • CloneTools (Place your VMware Optimizer and templates in that location.)

This has been tested on Win7 and Win10 running PowerShell version 5.1. The reason I have not included the 3rd party update process is that each environment requires different features or specs. Some may want to disable some things where others may want to keep them on.

**Does make the call to run the Shutdown Script that was posted in post 4 of 4 HERE **

# Recompose Master Image (Step2)

# Chris Hildebrandt

# 10-19-2018

# Ver 1.5

# Script will Copy Files to Image, Enable SCCM service, Install SCCM updates, Wait X seconds, and Reboot

#______________________________________________________________________________________

#Varibles

$ScriptDate = Get-Date -Format MM-dd-yyyy

$ScriptDate2 = Get-Date -UFormat %m/%d/%y #Date used for custom Registry Key and Windows Updates Report

$FQDN = (Get-WmiObject win32_computersystem).DNSHostName+"."+(Get-WmiObject win32_computersystem).Domain

$LogLocation = "C:\VDI_Tools\Logs\$ScriptDate\$FQDN"

$SleepTimeInS = "60" #Sleep time in seconds

$AdobeUpdate = "C:\VDI_Tools\Scripts\AdobeAcrobatUpdate.ps1" #Adobe Acrobat Update Script

$FlashUpdate = "C:\VDI_Tools\Scripts\FlashUpdate.ps1" #Flash Update Script

$FirefoxUpdate = "C:\VDI_Tools\Scripts\FireFoxUpdate.ps1" #Firefox Update Script

$JavaUpdate = "C:\VDI_Tools\Scripts\JavaUpdate.ps1" #Java Update Script

$ShutdownScript = "C:\VDI_Tools\Scripts\ShutdownScript.ps1" #Shutdown Script Location

$CustomRegPath = "HKLM:\Software\Company\Horizon\" #Custom Registry Location. Replace Company with your Company Name

#______________________________________________________________________________________

#Copy Locations

$CopyshareLocation = "\\share\VDI_Tools\" #Remote Share location

$DestinationLocation = "C:\VDI_Tools\"

#______________________________________________________________________________________

#Start the Debug Logging

$ErrorActionPreference="SilentlyContinue"

Stop-Transcript | out-null

$ErrorActionPreference = "Continue"

Start-Transcript -path "$loglocation\RecomposeMSTRInstallUpdates_$ScriptDate.txt"

#______________________________________________________________________________________

# Define functions

#______________________________________________________________________________________

# Import Function Get-Software
# Created By Boe Prox HTTPS://MCPMAG.COM

Function Get-Software {

[OutputType('System.Software.Inventory')]

[Cmdletbinding()]

Param(

[Parameter(ValueFromPipeline=$True,ValueFromPipelineByPropertyName=$True)]

[String[]]$Computername=$env:COMPUTERNAME

)

Begin {

}

Process {

ForEach ($Computerin$Computername){

If (Test-Connection-ComputerName $Computer-Count 1-Quiet) {

$Paths=@("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall","SOFTWARE\\Wow6432node\\Microsoft\\Windows\\CurrentVersion\\Uninstall")

ForEach($Pathin$Paths) {

Write-Verbose"Checking Path: $Path"

# Create an instance of the Registry Object and open the HKLM base key

Try {

$reg=[microsoft.win32.registrykey]::OpenRemoteBaseKey('LocalMachine',$Computer,'Registry64')

} Catch {

Write-Error$_

Continue

}

# Drill down into the Uninstall key using the OpenSubKey Method

Try {

$regkey=$reg.OpenSubKey($Path)

# Retrieve an array of string that contain all the subkey names

$subkeys=$regkey.GetSubKeyNames()

# Open each Subkey and use GetValue Method to return the required values for each

ForEach ($keyin$subkeys){

Write-Verbose"Key: $Key"

$thisKey=$Path+"\\"+$key

Try {

$thisSubKey=$reg.OpenSubKey($thisKey)

# Prevent Objects with empty DisplayName

$DisplayName=$thisSubKey.getValue("DisplayName")

If ($DisplayName-AND$DisplayName-notmatch'^Update for|rollup|^Security Update|^Service Pack|^HotFix') {

$Date=$thisSubKey.GetValue('InstallDate')

If ($Date) {

Try {

$Date= [datetime]::ParseExact($Date,'yyyyMMdd',$Null)

} Catch{

Write-Warning"$($Computer): $_ <$($Date)>"

$Date=$Null

}

}

# Create New Object with empty Properties

$Publisher=Try {

$thisSubKey.GetValue('Publisher').Trim()

}

Catch {

$thisSubKey.GetValue('Publisher')

}

$Version=Try {

#Some weirdness with trailing [char]0 on some strings

$thisSubKey.GetValue('DisplayVersion').TrimEnd(([char[]](32,0)))

}

Catch {

$thisSubKey.GetValue('DisplayVersion')

}

$UninstallString=Try {

$thisSubKey.GetValue('UninstallString').Trim()

}

Catch {

$thisSubKey.GetValue('UninstallString')

}

$InstallLocation=Try {

$thisSubKey.GetValue('InstallLocation').Trim()

}

Catch {

$thisSubKey.GetValue('InstallLocation')

}

$InstallSource=Try {

$thisSubKey.GetValue('InstallSource').Trim()

}

Catch {

$thisSubKey.GetValue('InstallSource')

}

$HelpLink=Try {

$thisSubKey.GetValue('HelpLink').Trim()

}

Catch {

$thisSubKey.GetValue('HelpLink')

}

$Object= [pscustomobject]@{

Computername=$Computer

DisplayName=$DisplayName

Version=$Version

InstallDate=$Date

Publisher=$Publisher

UninstallString=$UninstallString

InstallLocation=$InstallLocation

InstallSource=$InstallSource

HelpLink=$thisSubKey.GetValue('HelpLink')

EstimatedSizeMB= [decimal]([math]::Round(($thisSubKey.GetValue('EstimatedSize')*1024)/1MB,2))

}

$Object.pstypenames.insert(0,'System.Software.Inventory')

Write-Output$Object

}

} Catch {

Write-Warning"$Key : $_"

}

}

} Catch {}

$reg.Close()

}

} Else {

Write-Error"$($Computer): unable to reach remote system!"

}

}

}

}

#______________________________________________________________________________________

# Start Services Function

function Start-VDIservices ($SVCname) {

Set-Service-Name $svcname-StartupType Automatic

Start-Service-Name $svcname

Do {

$svc=Get-Service-Name $svcname

Start-Sleep2

} While ( $svc.Status -ne "Running" )

}

#______________________________________________________________________________________

#Check is VDI Masters Account Password file exists

if(-Not (Test-Path -Path "C:\VDI_Tools\Scripts\VDIMastersPassword.txt" ))

{

Write-Host"Credentials file not found proceeding to creation"

#______________________________________________________________________________________

#Create Secure Password File

Get-Credential-Message "Enter VDI Master Admin Account Domain\Username"|Export-Clixml"C:\VDI_Tools\Scripts\VDIMastersPassword.txt"

write-host"Create Credentials File by prompting for creds"

}

#______________________________________________________________________________________

#Import Secure Creds for use.

$VDIMSTRCreds = Import-Clixml "C:\VDI_Tools\Scripts\VDIMastersPassword.txt"

write-host "Import Secure Creds"

#______________________________________________________________________________________

#Copy Clone Tools and Scripts

Copy-Item $CopyshareLocation -Destination $DestinationLocation -Recurse

write-host "Copy Files from Share"

#______________________________________________________________________________________

# Capture installed Software before updating

get-software | Format-Table | Out-File "$LogLocation\InstalledSoftwareBefore.txt"

write-host "Capture Installed Software Prior to update and save to Logs Folder"

#______________________________________________________________________________________

#Check to see if machine is managed by SCCM

$CheckSCCM = get-wmiobject win32_Service | Where-Object {$_.Name -eq "CCMexec"} | Select-Object Name

write-host "Check if VM is managed by SCCM"

#______________________________________________________________________________________

#If Else statement based on if VM is managed by SCCM

if($CheckSCCM.Name -eq 'CCMexec')

{

write-host"VM is managed by SCCM"

#______________________________________________________________________________________

#Check Status of SCCM service

$SCCMStatus=get-service-name CCMexec |Select-Object Name,Status,Starttype

if($SCCMStatus.Status-eq'Stopped')

{

#______________________________________________________________________________________

#Set SCCM to Automatic and Start Service

Start-VDIservices CCMexec

write-host"Start SCCM Services"

}

#______________________________________________________________________________________

#Start SCCM Patching

$AppEvalState0="0"

$AppEvalState1="1"

$Application= (Get-WmiObject-Namespace "root\ccm\clientSDK"-Class CCM_SoftwareUpdate |Where-Object { $_.EvaluationState-like"*$($AppEvalState0)*"-or$_.EvaluationState-like"*$($AppEvalState1)*"})

Invoke-WmiMethod-Class CCM_SoftwareUpdatesManager -Name InstallUpdates -ArgumentList (,$Application) -Namespace root\ccm\clientsdk

write-host"Invoke SCCM install of Updates"

#______________________________________________________________________________________

#Pause Script to wait for Updates to install

Start-Sleep-Seconds $SleepTimeInS

write-host"Start Sleep Timer"




}

else

#______________________________________________________________________________________

#Else statemet for installing Windows updates through windows updates, and making external calls to other scripts to update

{

#______________________________________________________________________________________

#Check Status of Windows Update service

$WindowsUpdatateStatus=get-service-name wuauserv |Select-Object Name,Status,Starttype

if($WindowsUpdatateStatus.Status-eq'Stopped')

{

#______________________________________________________________________________________

#Set Windows Update to Automatic and Start Service

Start-VDIservices wuauserv

write-host"Started Windows Update Service"

}

#______________________________________________________________________________________

#Install 3rd Party updates (Java, Flash, Adobe Reader, Chrome, Firefox)

#______________________________________________________________________________________

#Check to see if Adobe Flash is installed

if ((Get-Software|Select-Object DisplayName) -like"*Flash*")

{

#______________________________________________________________________________________

#Install Flash Updates

Invoke-Expression$FlashUpdate

write-host"Installed Flash Update"

}




#______________________________________________________________________________________

#Check to see if Adobe Acrobat is installed

if ((Get-Software|Select-Object DisplayName) -like"*Acrobat Reader*")

{

#______________________________________________________________________________________

#Install Adobe Updates

Invoke-Expression$AdobeUpdate

write-host"Installed Adobe Acrobat Update"

}




#______________________________________________________________________________________

#Check to see if Firefox is installed

if ((Get-Software|Select-Object DisplayName) -like"*Mozilla Firefox*")

{

#______________________________________________________________________________________

#Install Firefox Updates

Invoke-Expression$FirefoxUpdate

write-host"Installed Firefox Update"

}




#______________________________________________________________________________________

#Check to see if Java is installed

if ((Get-Software|Select-Object DisplayName) -like"*Java*")

{

#______________________________________________________________________________________

#Install Java Updates

Invoke-Expression$JavaUpdate

write-host"Installed Java Update"

}

#______________________________________________________________________________________

#Check to see if Chrome is installed

if ((Get-Software|Select-Object DisplayName) -like"*Chrome*")

{

#______________________________________________________________________________________

#Install Chrome Updates

C:\"Program Files (x86)\Google\Update\GoogleUpdate.exe" /ua /installsource scheduler

write-host"Installed Google Chrome Update"

}

#______________________________________________________________________________________

#Prep for windows updates and Install updates

#______________________________________________________________________________________

#Hide Windows updates labled as "Preview"

Hide-WindowsUpdate-Title "Preview*"-Confirm:$false

write-host"Hide Windows Updates labled as Preview"

#______________________________________________________________________________________

#Auto instal windows updates

Get-WUInstall –AcceptAll -install

write-host"Install Windows Updates"




#______________________________________________________________________________________

#Start Sleep timer for update installs.

# Start-Sleep -Seconds $SleepTimeInS

write-host"Wait for Windows Updates to install"

}

#______________________________________________________________________________________

#Decrypt Password for imput into reg.

$VDIMSTRTextCreds = $VDIMSTRCreds.Password | ConvertFrom-SecureString

$VDIMSTRTextCredsPlain = [Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR( (ConvertTo-SecureString $VDIMSTRTextCreds) ))

#______________________________________________________________________________________

#Set AutoLog on and auto kick off step3

$RegPath = "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon"

$RegROPath = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce"

Set-ItemProperty $RegPath "AutoAdminLogon" -Value "1" -type String

write-host"Set Registry Key for AutoLogon"

Set-ItemProperty $RegPath "DefaultUsername" -Value $VDIMSTRCreds.UserName -type String

write-host"Set Registry Key for AutoLogon Username"

Set-ItemProperty $RegPath "DefaultPassword" -Value $VDIMSTRTextCredsPlain -type String

write-host "Set Registry Key for AutoLogon Password"

Set-ItemProperty $RegROPath "(Default)" -Value "c:\WINDOWS\system32\WindowsPowerShell\v1.0\powershell.exe -executionpolicy unrestricted -file $ShutdownScript" -type String

write-host "Set Registry Key for AutoLogon Logon Script"

#______________________________________________________________________________________

#Remote registry keys for legal notice

Remove-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System" -Name "legalnoticecaption"

Write-Host "Remove Legal Notice Title Registry Key"

Remove-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System" -Name "legalnoticetext"

Write-Host "Remove Legal Notice Text Registry Key "

#______________________________________________________________________________________

# Capture installed Software before updating

get-software | Format-Table | Out-File "$LogLocation\InstalledSoftwareAfter.txt"

write-host "Capture Installed Software After update and save to Logs Folder"

#______________________________________________________________________________________

# Check if Custom REG key is created if not create LastRecomposeDate REG key with 1/1/2000 date

if ( -not (Test-Path -Path $CustomRegPath)) {New-Item -path $CustomRegPath -Force | New-ItemProperty -Name LastRecomposeDate -Value "01/01/2000" -Force | Out-Null}

write-host "Check if Custom key has been created and if not create registry key and set value to 1-1-2000"

#______________________________________________________________________________________

# Get LastRecomposeData value

$LastRecomposeDate = Get-ItemProperty -Path $CustomRegPath -Name LastRecomposeDate | Select-Object -ExpandProperty LastRecomposeDate

write-host "Get Last recompose date from custom Registry key, Last Recompose date was $LastRecomposeDate"

#______________________________________________________________________________________

# Get Windows Updates installed since last recompose.

Get-HotFix | Where-Object -Property InstalledOn -GT $LastRecomposeDate | Out-File -FilePath "$LogLocation\WindowsUpdates.txt"

write-host "Create TXT file with Installed updates as of $LastRecomposeDate"

#______________________________________________________________________________________

#Set REG Key to store Last Recompose Date

Set-ItemProperty -Path $CustomRegPath -Name LastRecomposeDate -Value "$ScriptDate2" -Force

write-host "Set Last Recompose date to $ScriptDate2"

#______________________________________________________________________________________

# Stop Logging

Stop-Transcript

#______________________________________________________________________________________

#Reboot VM

Shutdown -r

Over the next few weeks, I hope to have part 2 and Part 1 out on this process and compile the entirety of the recompose script. Again if you find anything wrong or missing please let me know.

**As security disclaimer that when their Script adds the registry key for auto log on password it is saved in plain text. Will be editing the Shutdown script to remove the key when the machine finished its reboot and calls the shutdown script.

Please read the 4 of 4 located HERE

Posted in Blogtober, CLI and Powershell, DevOps, VDI, Virtulization | Tagged , , , ,

Building a Shutdown/Clean-up script for post-installation of updates. Part 4 of 4 DevOps meets VMware Horizon. Automating the daily busy tasks out of the way.

Many people have created Shutdown or clean up scripts for the Linked Clone or Instant Clone VMs before, I am trying to do something a little different. I have been working on automating the process of Recompose or Image push from start to finish and here is my rendition of the Shutdown/Cleanup script in PowerShell. This is the last piece of a 3-piece script. I have delayed this rollout due to wanting to clean up the script and add some better logging with valid error logs.

So what have I done? I originally ran just a BAT script that worked well, but with times changing and wanting to do more in PowerShell, So I opted to tackle it this way. This gave the ability to do some logging with it and do more integration into other scripts if I decided.

Functions performed by the script:

  • Checks the version of windows. (This is used for the optimizer at the end.)
  • Runs Disk Cleanup
  • Runs Defragment
  • Pre-Compile .NET Framework
  • Runs SEP update, Scan, Forced Check-in, and Clone Prep.
  • Runs VMware Optimizer based on what OS version you are on and what templates you have defined. (Beings its ran via CMD there are no rolling back changes, it’s a bug/Feature in the Optimizer)
  • Disables or Stops Services like Windows Updates, SCCM, Adaptiva. AppVolumes
  • Clean out Downloads Cache folder
  • Clean out Windows Prefetch
  • Clear the event logs
  • Releases IP Addresses
  • Clears DNS
  • Shutdown the VM

These are the normal processes we run through when preparing a Master image for cloning. This has been tested on Win7, Win8, and Win10 running PowerShell version 5.1. Please pay attention to the folder structure and the template naming structure for the optimizer. I will eventually build an install package for this but will take some time.

For logs and files to save correctly I am using a nested file Structure of:

  • C:\VDI_Tools
    • Logs (This is where all the log files will be saved)
    • Scripts (Save this script and run it from this location)
    • CloneTools (Place your VMware Optimizer and templates in that location.)
# Shutdown Script to Prepare Master Image for Recompose or Image Push

# Chris Hildebrandt

# 10-18-2018

# Ver 1.5

# Script will Run Disk Cleanup, Defrag, Run SEP scan force update, Run Optimizer, and shutdown services, clear DNS and IP, and Shutdown VM

#______________________________________________________________________________________

# Check Version of OS

$winVersion = "0"

$OSVersion = [System.Environment]::OSVersion.Version

if ($OSversion.Major -eq "10" -and $OSversion.Minor -eq "0"){ $winVersion = "10" }

elseif ($OSversion.Major -eq "6" -and $OSversion.Minor -eq "2") { $winVersion = "8" }

elseif ($OSversion.Major -eq "6" -and $OSversion.Minor -eq "1") { $winVersion = "7" }

Write-host "Windows Version is $winVersion"

#______________________________________________________________________________________

# Varibles

$ScriptDate = Get-Date -Format MM-dd-yyyy

$FQDN = (Get-WmiObject win32_computersystem).DNSHostName+"."+(Get-WmiObject win32_computersystem).Domain

$LoginFile = "C:\VDI_Tools\Logs\$ScriptDate\$FQDN\ShutdownScript_$ScriptDate.txt"

$defragbat = "C:\VDI_Tools\Scripts\dfrag.bat"       #Temporary Location where it saves the Defrag.bat file.

#______________________________________________________________________________________

# Optimizer Varibles

$RunOptimizer = 1   # To run Optimizer enter 1, If you do not want to run enter 0

$OptimizerLocation = "C:\VDI_Tools\CloneTools\VMwareOSOptimizationTool"

$OptimizerTemplate = "C:\VDI_Tools\CloneTools\ARDx_Windows${winVersion}.xml"

$OptimizerLogLocation = "C:\VDI_Tools\Logs\$ScriptDate\$FQDN\"

$OptimizerErrorLogLocation = "C:\VDI_Tools\Logs\$ScriptDate\$FQDN\OptimizationErrorLog.txt"

$OptimizerbatFile = "C:\VDI_Tools\Scripts\Optimizer.$fqdn.bat"         #Temporary Location where it saves the Optimizer.bat file.

$RunSEP = 1 # To run Optimizer enter 1, If you do not want to run enter 0

#______________________________________________________________________________________

# Start the Debug Logging

$ErrorActionPreference="SilentlyContinue"

Stop-Transcript | out-null

$ErrorActionPreference = "Continue"

Start-Transcript -path $LoginFile

Write-host "Windows Version is $winVersion"

#______________________________________________________________________________________

# Define functions

#______________________________________________________________________________________

# Start Services Function

function Start-VDIservices ($SVCname) {

    Set-Service -Name $svcname -StartupType Automatic

    Start-Service -Name $svcname

    Do {

    $svc = Get-Service -Name $svcname

    Start-Sleep 2

    } While ( $svc.Status -ne "Running" )

    }

#______________________________________________________________________________________

# Stop Services Function

function Stop-VDIservices ($SVCname) {

    Stop-Service -Name $svcname

    Do {

    $svc = Get-Service -Name $svcname

    Start-Sleep 2

    } While ( $svc.Status -ne "Stopped" )

    Set-Service -Name $svcname -StartupType disabled

    }

#______________________________________________________________________________________

# Do while proc Like Function

function DoWhile-LikeProcName ($Process) {

    Do

    {

    "Processes $Process is still running"

    $proc = Get-Process

    start-sleep 10

    } While ($proc.name -like "*$Process*")

    }

#______________________________________________________________________________________

# Do while Proc Equals Function

function DoWhile-EQProcName ($Process) {

    Do

    {

    "Processes $Process is still running"

    $proc = Get-Process

    start-sleep 10

    } While ($proc.name -eq "$Process")

    }    




#______________________________________________________________________________________

# Run Disk Cleanup to remove temp files, empty recycle bin and remove other unneeded files

Start-Process -Filepath "c:\windows\system32\cleanmgr" -argumentlist '/sagerun:1'

Write-host "Running Disk Cleanup"

#______________________________________________________________________________________

# Check status of Disk Cleanup

DoWhile-LikeProcName cleanmgr

#______________________________________________________________________________________

# Check status of Dfrag Service if stopped start.

$DfragStatus = get-service -name defragsvc | Select-Object Name,Status,Starttype

if($DfragStatus.Status -eq 'Stopped')

{

#______________________________________________________________________________________

# Start Defrag Service

Start-VDIservices defragsvc

Write-host "Start Defrag Service"

}

#______________________________________________________________________________________

# Create Temp Defrag BAT

"defrag c: /U /V" | Set-Content $defragbat

Write-host "Create Defrag Temp .bat file"

#______________________________________________________________________________________

# Start Defrag BAT

Start-Process $defragbat

Write-host "Running Defrag Cleanup"

#______________________________________________________________________________________

# Check status of Disk Defrag

DoWhile-EQProcName Defrag

#______________________________________________________________________________________

# Remove Temp Defrag BAT

Remove-Item -Path $defragbat

Write-host "Delete Defrag Temp .bat file"

#______________________________________________________________________________________

# Stop Defrag Service

Stop-VDIservices defragsvc

Write-host "Stop Defrag Service"

#______________________________________________________________________________________

# Pre-complile .NET framework Assemblies

Start-Process -Filepath "C:\Windows\Microsoft.NET\Framework\v4.0.30319\ngen.exe" -argumentlist 'update','/force'

New-ItemProperty -Name verbosestatus -Path "HKLM:\Software\Microsoft\Windows\CurrentVersion\Policies\System" -Force -PropertyType DWORD -Value "1"

Write-host "Running a Pre-Compile of .NET Framework"

#______________________________________________________________________________________

# Check status of the .NET recompile

DoWhile-LikeProcName ngen

#______________________________________________________________________________________ 

# Run SEP update and Cleanup

if($RunSEP -eq '1')

{

    Write-host "Starting the SEP Update and Cleanup Process"

#______________________________________________________________________________________

# Check to see if SEP is installed and prep for Recompose

#______________________________________________________________________________________

# If Check for SEP install

$CheckSEP = get-service -name SepMasterService | Select-Object Name,Status,Starttype

if($CheckSEP.Name -eq 'SepMasterService')

    {

    #______________________________________________________________________________________

    # Force SEP checking and Update

    Start-Process -Filepath "C:\Program Files (x86)\Symantec\Symantec Endpoint Protection\SepLiveUpdate.exe"

    Write-host "Liveupdate SEP Config"

    #______________________________________________________________________________________ 

    # Check status of SEP Update

    DoWhile-LikeProcName SepLiveUpdate

    #______________________________________________________________________________________ 

    # Run full SEP System scan

    Start-Process -Filepath "C:\Program Files (x86)\Symantec\Symantec Endpoint Protection\doScan.exe" -argumentlist '/scanname "Full System Scan"'

    Write-host "Run Full System SEP Scan"

    #______________________________________________________________________________________ 

    # Check status of SEP Scan

    DoWhile-LikeProcName doscan

    #______________________________________________________________________________________ 

    # Force SEP checking and Update

    Start-Process -Filepath "C:\Program Files (x86)\Symantec\Symantec Endpoint Protection\smc.exe" -argumentlist '-updateconfig'

    Write-host "Update SEP and force Checkin to report the recent completed Scan"

    #______________________________________________________________________________________ 

    # Check status of SEP Update

    DoWhile-LikeProcName doscan

    #______________________________________________________________________________________

    # Check status of Symantec Endpoint Protection Service.

    $SEPStatus = get-service -name SepMasterService | Select-Object Name,Status,Starttype

    if($SEPStatus.Status -eq 'Running')

    {

        #______________________________________________________________________________________

        # Stop SEP Service and Prep for Clone

        Start-Process -Filepath "C:\Program Files (x86)\Symantec\Symantec Endpoint Protection\smc.exe" -argumentlist '-stop'

        Write-host "Stopped SEP Service"

    }

        $filesToClean = @("C:\sephwid.xml","C:\communicator.dat","C:\Program Files\Common Files\Symantec Shared\HWID\sephwid.xml","C:\Program Files\Common Files\Symantec Shared\HWID\communicator.dat", "C:\Windows\Temp\sephwid.xml","C:\Windows\Temp\communicator.dat","C:\Documents and Settings\*\Local Settings\Temp\sephwid.xml","CC:\Documents and Settings\*\Local Settings\Temp\communicator.dat","C:\Users\*\AppData\Local\Temp\sephwid.xml","C:\Users\*\AppData\Local\Temp\communicator.dat")

        foreach($file in $filesToClean)

        {

            if(Test-Path $file) {Remove-Item -Path $file -Recurse -force}

            Write-Host "Removed File $File"

        }




        #______________________________________________________________________________________ 

        # Remove SEP Reg Keys

        #______________________________________________________________________________________ 

        # Remove 32bit SEP Reg Keys

        if (Test-Path -Path "HKLM:\SOFTWARE\Symantec\Symantec Endpoint Protection\SMC\SYLINK\SyLink")

        {

            #Remove-ItemProperty -Path "HKLM:\SOFTWARE\Symantec\Symantec Endpoint Protection\SMC\SYLINK\SyLink" -Name "ForceHardwareKey"

            Remove-ItemProperty -Path "HKLM:\SOFTWARE\Symantec\Symantec Endpoint Protection\SMC\SYLINK\SyLink" -Name "HardwareID"

            Remove-ItemProperty -Path "HKLM:\SOFTWARE\Symantec\Symantec Endpoint Protection\SMC\SYLINK\SyLink" -Name "HostGUID"

            Write-host "Removed SEP 32 Bit Reg Keys"

        }

        if (Test-Path -Path "HKLM:\SOFTWARE\WOW6432Node\Symantec\Symantec Endpoint Protection\SMC\SYLINK\SyLink")

        {

            #Remove-ItemProperty -Path "HKLM:\SOFTWARE\WOW6432Node\Symantec\Symantec Endpoint Protection\SMC\SYLINK\SyLink" -Name "ForceHardwareKey"

            Remove-ItemProperty -Path "HKLM:\SOFTWARE\WOW6432Node\Symantec\Symantec Endpoint Protection\SMC\SYLINK\SyLink" -Name "HardwareID"

            Remove-ItemProperty -Path "HKLM:\SOFTWARE\WOW6432Node\Symantec\Symantec Endpoint Protection\SMC\SYLINK\SyLink" -Name "HostGUID"

            Write-host "Removed SEP 64 Bit Reg Keys"

         }




    }

    Write-host "Finished SEP Cleanup and Prep for Cloneing"

}

#______________________________________________________________________________________ 

# Run VMware optimizer

if($RunOptimizer -eq '1')

{

    Write-host "Prepairing to run VMware Optimizer"

#______________________________________________________________________________________ 

# Create Temp Bat file to optimize OS

@"

start /wait $OptimizerLocation -t $OptimizerTemplate -r $OptimizerLogLocation

start /wait $OptimizerLocation -o recommended -t $OptimizerTemplate -v > $OptimizerErrorLogLocation 2>&1

start /wait $OptimizerLocation -t $OptimizerTemplate -r $OptimizerLogLocation

"@ | Set-Content $OptimizerbatFile

Write-host "Built Temporary VMware Optimizer .BAT file based on Template $OptimizerTemplate"

    #______________________________________________________________________________________ 

    # Run BAT file to optimize OS

    Start-Process $OptimizerbatFile

    Write-host "Running VMware Optimizer .BAT based on Template $OptimizerTemplate"

    #______________________________________________________________________________________ 

    # Check status of the Optimize and pause

    DoWhile-EQProcName CMD

    #______________________________________________________________________________________ 

    # Remove Temp BAT file

    Remove-Item -Path $OptimizerbatFile

    Write-host "Removing Temporary VMware Optimizer .BAT file"

}

#______________________________________________________________________________________

# Check if VM is managed by SCCM

$CheckSCCM = get-wmiobject win32_Service | Where-Object {$_.Name -eq "CCMexec"} | Select-Object Name

Write-host "Check if SCCM service is installed"

#______________________________________________________________________________________

# If Else statement based on if VM is managed by SCCM

if($CheckSCCM.Name -eq 'CCMexec')

{

    Write-host "VM is managed by SCCM"

    #______________________________________________________________________________________

    # Check status of SCCM Service.

    $SCCMStatus = get-service -name "CCMexec" | Select-Object Status

    Write-host "Check if SCCM service CCMexec is running"

    if($SCCMStatus.Status -eq 'Running')

        {   

            Stop-VDIservices CCMexec

            Write-host "Stop SCCM Service CCMexec"

        }




    #______________________________________________________________________________________

    # Check status of SCCM Service if stopped start.

    $AdaptivaStatus = get-service -name "adaptivaclient" | Select-Object Status

    Write-host "Check if Adaptiva Client is installed"

    if($AdaptivaStatus.Status -eq 'Running')

        {

            Stop-VDIservices adaptivaclient

            Write-host "Stop Adaptiva Services"

        }

}

#______________________________________________________________________________________

# Check status of Windows Update Service.

$WindowsUpdatateStatus = get-service -name wuauserv | Select-Object Name,Status,Starttype

if($WindowsUpdatateStatus.Status -eq 'Running')

    {

        #______________________________________________________________________________________

        # Stop Windows Update Service

        Stop-VDIservices wuauserv

        Write-host "Stop Windows Update Services"

    }

#______________________________________________________________________________________

# Clean out widows downloads cache

if (Test-Path "C:\Windows\SoftwareDistribution\Download\")

{

Remove-Item -Path "C:\Windows\SoftwareDistribution\Download\*" -force -Recurse

Write-host "Clean Out Windows Update Download Cache"

}

#______________________________________________________________________________________

# Check status of VSS Service if stopped start.

$DfragStatus = get-service -name VSS | Select-Object Name,Status,Starttype

if($DfragStatus.Status -eq 'Running')

{

    #______________________________________________________________________________________

    # Clean out Volume Shadow Copys

    .\vssadmin.exe delete shadows /all

    Write-host "Clean out Volume Shadow Copys"

}

#______________________________________________________________________________________

# Clean out Windows PreFetch

if (Test-Path "c:\Windows\Prefetch\")

{

Remove-item -Path "c:\Windows\Prefetch\*.*" -Force

Write-host "Remove windows Prefetch files"

}

#______________________________________________________________________________________

# Check status if AppVolumes is installed and stop the service.

$AppVStatus = get-service | Select-Object Name,Status,Starttype

if($AppVStatus.Name -eq 'svservice')

{

    Stop-Service -Name svservice

    Write-host "Stop AppVolumes Service"

}

#______________________________________________________________________________________

# Clear all event logs

wevtutil el | Foreach-Object {wevtutil cl "$_"}

Write-host "Clear Windows Event Logs"

#______________________________________________________________________________________

# Stop Logging

Stop-Transcript

#______________________________________________________________________________________

# Release IP, Flush DNS and Shutdown VM

ipconfig /release

ipconfig /flushdns

shutdown -s
I will continue to update and optimize this code so check back frequently. If There are any issues found please let me know.
Posted in Blogtober, CLI and Powershell, DevOps, VDI, Virtulization | Tagged , , , ,