Author Archives: IS Solutions

Managing AD Account State

Sometimes the situation arise where an AD account is enabled or disabled by mistake or earlier then its required time. With a lot of companies using automated solutions for managing this, reverting the change can take time. Such urgent situations can be handled by using scripts which could automatically revert the changes to account (enable/disable) every x minutes. All you need to do is to schedule the scripts below to run every x minutes as per your requirements and then keep the input files updated with IDs to action on. Once the changes are permanently fixed, remove the IDs from the input files.  Create two .txt files in say C:\Temp\ folder which would act as input files. One for enable ID and one for disable ID scripts. Populate the required samAccountNames in the relevant files.

Enable IDs:

Option Explicit
On Error Resume Next
Dim ranpass, i, digit, final, Usr, Usrpass, AlphaID, NumericID, EnaUser
Dim oFSO, sFile, oFile, sText, pResult, oFilesys, oFiletxt, sFilename, sPath
Dim objRootDSE, strDNSDomain, objTrans, strNetBIOSDomain
Dim strUserDN, objUser, strempid, strUserNTName, aLine
Set oFSO = CreateObject("Scripting.FileSystemObject")
Set oFilesys = CreateObject("Scripting.FileSystemObject")
Set oFiletxt = oFilesys.OpenTextFile("C:\Temp\EnabledIDs.txt",8,True) 
sFile = "C:\Temp\Userlistenable.txt"
If oFSO.FileExists(sFile) Then
Set oFile = oFSO.OpenTextFile(sFile, 1)
 Do While Not oFile.AtEndOfStream
  sText = oFile.ReadLine
  aLine = split(sText, ",")
   If Trim(sText) <> "" Then
                DisableEmp aLine(0)
   End If
 Loop
oFile.Close
Else
WScript.Echo "The file was not there."
End If
Sub DisableEmp (NumericID)
Dim strUserName,strDomain, objSystemInfo
Set objSystemInfo = CreateObject("ADSystemInfo")
strDomain = objSystemInfo.DomainShortName
strUserName = NumericID
DisableUser strUserName,strDomain
End Sub
sub DisableUser(UserName,DomainName)
Dim strDisableAccount
strUserDN = GetUserDN(UserName,DomainName)
strDisableAccount = FALSE
' ------ END CONFIGURATION ---------
set objUser = GetObject("LDAP://" & strUserDN)
if objUser.AccountDisabled = TRUE then
                objUser.AccountDisabled = strDisableAccount
                objUser.SetInfo
                Outfile UserName
end if
end sub
Function GetUserDN(strUserName,strDomain)
                Set objTrans = CreateObject("NameTranslate")
                objTrans.Init 1, strDomain
                objTrans.Set 3, strDomain & "\" & strUserName
                strUserDN = objTrans.Get(1)
                GetUserDN = strUserDN
end function
Sub Outfile(EnaUser)
                oFiletxt.WriteLine(EnaUser)
End Sub

Disable IDs:

Option Explicit
On Error Resume Next
Dim ranpass, i, digit, final, Usr, Usrpass, AlphaID, NumericID, EnaUser
Dim oFSO, sFile, oFile, sText, pResult, oFilesys, oFiletxt, sFilename, sPath
Dim objRootDSE, strDNSDomain, objTrans, strNetBIOSDomain
Dim strUserDN, objUser, strempid, strUserNTName, aLine
Set oFSO = CreateObject("Scripting.FileSystemObject")
Set oFilesys = CreateObject("Scripting.FileSystemObject")
Set oFiletxt = oFilesys.OpenTextFile("C:\Temp\DisabledIDs.txt",8,True) 
sFile = "C:\Temp\Userlist.txt"
If oFSO.FileExists(sFile) Then
Set oFile = oFSO.OpenTextFile(sFile, 1)
 Do While Not oFile.AtEndOfStream
  sText = oFile.ReadLine
  aLine = split(sText, ",")
   If Trim(sText) <> "" Then
                DisableEmp aLine(0)
   End If
 Loop
oFile.Close
Else
WScript.Echo "The file was not there."
End If
Sub DisableEmp (NumericID)
Dim strUserName,strDomain, objSystemInfo
Set objSystemInfo = CreateObject("ADSystemInfo")
strDomain = objSystemInfo.DomainShortName
strUserName = NumericID
DisableUser strUserName,strDomain
End Sub
sub DisableUser(UserName,DomainName)
Dim strDisableAccount
strUserDN = GetUserDN(UserName,DomainName)
strDisableAccount = TRUE
' ------ END CONFIGURATION ---------
set objUser = GetObject("LDAP://" & strUserDN)
if objUser.AccountDisabled = FALSE then
                objUser.AccountDisabled = strDisableAccount
                objUser.SetInfo
                Outfile UserName
end if
end sub
Function GetUserDN(strUserName,strDomain)
                Set objTrans = CreateObject("NameTranslate")
                objTrans.Init 1, strDomain
                objTrans.Set 3, strDomain & "\" & strUserName
                strUserDN = objTrans.Get(1)
                GetUserDN = strUserDN
end function
Sub Outfile(EnaUser)
                oFiletxt.WriteLine(EnaUser)
End Sub

This can be scheduled on one of the domain controllers.

Custom post patching reboot notifications

One of the pain points for system administrators today is to maintain the compliance of patching which often cannot be achieved without reboot of end user devices. Although various patching tools do provide these features but often they are not flexible enough to meet each customer’s demand. Here is a little script created in AutoIT which could let you create a custom popup notification and you can schedule this as per your requirements using group policies. Such tools are often useful where you cannot force reboot user systems.

Download and install AutoIT scripting tool. Use the following code and build a new .exe

TraySetToolTip ("System Update Checker") 
Break(0)
$ver = RegRead("HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\RebootRequired","")
If @Error < 0 then
 TrayTip("IT - System Updates", "Important system updates have been installed on your computer. Please save your work and restart your system as soon as possible.", 20, 1) 
 Sleep (10000) 
EndIf

Schedule the .exe above to run every hour (change frequency as required) on end user machines via group policy.

If new patches have been installed and a reboot is pending then following popup will appear on the tray reminding users to reboot asap.
Untitled.jpg

Once rebooted the script will not take any action and close silently. Do remember to customize as per your requirement.

Track VM Changes

With large VMware implementations and large teams to support those environments, it becomes difficult to track of all your team is following the company standards while changing any existing virtual machine or creating new ones. Following is a sample script on how you can track it automatically via emails. The parameters being tracked can be changed as per your individual environment standards. We will implement this on the Datacenter level with the vCenter, however, you can change the target as per your requirement.

Prerequisite: Sending SMTP mails from your vCenter server should be configured.

First, identify the scenario which could indicate a machine configuration change or new machine creation, e.g. a VM is created, VM is configured or VM is powered off.

Now create a new alarm on the datacenter level within vCenter with following Events:

1

 

Create a folder under C:\Windows\ or any other preferred location and copy the following three scripts in it.

  1. Batch File:

Create a batch file createalert.bat with following content:

@echo off
C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe "C:\windows\folder\createalert.ps1 %1"
  1. Powershell Script:

Create a powershell script createalert.ps1 with following content:

param ([string] $srv = $null)
"C:\Program Files (x86)\VMware\Infrastructure\vSphere PowerCLI\Scripts\Initialize-PowerCLIEnvironment.ps1"
if (-not (Get-PSSnapin VMware.VimAutomation.Core -ErrorAction SilentlyContinue)) {
Add-PSSnapin VMware.VimAutomation.Core} 
$VC1 = "xxx.xxx.xxx.xxx"
$date = Get-Date -DisplayHint DateTime -Format "yyyy-M-d" 
Connect-VIServer -Server $VC1 -WarningAction SilentlyContinue
Get-VM $srv |Select Name,PowerState, vmhost, memoryGB, numcpu, notes, folder, resourcepool, version, 
@{N="Guest OS";E={ ($_ | Get-vmguest).osfullName}},
@{N="Cluster";E={ ($_ | Get-Cluster).Name}}, 
@{N="Network Name";E={ foreach($nic in $_ | Get-networkadapter){$nic.name}}},
@{N="Network Type";E={ foreach($nic in $_ | Get-networkadapter){$nic.type}}},
@{N="IP Address";E={ ($_ | Get-vmguest).ipaddress}},
@{N="Datastore Name";E={ ($_ | Get-Datastore).Name}},
@{N="DS Size GB";E={ ($_ | Get-Datastore).CapacityGB}},
@{N="DS Free Space GB";E={ ($_ | Get-Datastore).FreespaceGB}},
@{N="Total Disk GB"; E={  ($_ | Get-harddisk | measure-object -prorerty CapacityGB -sum).Sum}}, 
@{N="Hard Disks";E={ foreach($disk in $_ | Get-harddisk){$disk.name}}},
@{N="Disk Size GB";E={ foreach($disk in $_ | Get-harddisk){$disk.capacityGB}}},
@{N="Disk Format";E={ foreach($disk in $_ | Get-harddisk){$disk.storageformat}}},
@{N="Disk File Name";E={ foreach($disk in $_ | Get-harddisk){$disk.filename}}}|out-file c:\windows\folder\report\Report-$srv-$Date.txt -encoding ASCII
Disconnect-VIServer -server $VC1 -Force -Confirm:$false
cscript c:\windows\folder\sendmail.vbs "c:\windows\folder\report\Report-$srv-$Date.txt"
remove-item "c:\windows\folder\report\Report-$srv-$Date.txt"
  1. VBScript:

Create a vbscript sendmail.vbs with following content. You can also write the code below in powershell itself:

Const ForReading = 1
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objTextFile = objFSO.OpenTextFile (WScript.Arguments(0), ForReading)
Set objMessage = CreateObject("CDO.Message") 
objMessage.Subject = "VM State Alert"
objMessage.From = "sender@yourdomain.com" 
objMessage.To = "receiver@yourdomain.com"
objMessage.TextBody = "State of following VM has been changed. Please verify VM configuration:" & vbCRLF
objMessage.TextBody = objMessage.TextBody & objTextFile.ReadAll()
objTextFile.Close
objMessage.Configuration.Fields.Item ("http://schemas.microsoft.com/cdo/configuration/sendusing") = 2 
objMessage.Configuration.Fields.Item ("http://schemas.microsoft.com/cdo/configuration/smtpserver") = "smtp.yourdomain.com"
objMessage.Configuration.Fields.Item ("http://schemas.microsoft.com/cdo/configuration/smtpserverport") = 25
objMessage.Configuration.Fields.Update
objMessage.Send

Once all the three scripts are in place, reopen the alarm created earlier and add the following action:

2

Under configuration, mention the path of your batch file:

c:\windows\folder\createalert.bat %VMWARE_ALARM_EVENT_VM%

VMWARE_ALARM_EVENT_VM is a parameter which would pass the hostname of affected VM into the batch file.

Once this alert is configured and enabled, you will get an email every time a new VM is created or an existing VM is modified, and you can keep track of your organizational VM policies.

Decoding Powershell Help

Once you have found what command would serve your purpose (read here), you certainly need to understand its syntex and parameters. Help is one place where you can go and understand this, however, you need to know how to decode the help in powershell.

help commandlet or get-help commandlet

Capture

Here you see three different syntex in which this command can be run. All three can be used based on the input parameter values you have in hand.

Run help with -Full parameter to see more details for each parameter e.g. help get-service -FullCapture.JPG

You will notice there are different set of parenthesis around different variables. These can be read as follows:

  1. [] around parameter e.g. [-Name] signifies its positional i.e. you don’t need to type -Name to signify that you are specifying a name. You can simply type the value at its mentioned position e.g. 1 in this case.
  2. It entire parameter along with its value type is enclosed in [] then that parameter is optional. e.g. -DisplayName is required paramter whereas -ComputerName is not.

You can find more valuable input under the NOTES section in help specially the aliases that can be used for any particular command along with some examples.

Search Powershell Commands

With more and more technologies starting to use Powershell for its operations, sometimes it becomes difficult to find command for a particular task. Now when you launch Powershell, some of the modules are by default loaded into the memory based on operating system being used but some of them are not loaded by default although they are available on the computer. The good thing is, if you run a command from a module not loaded, it will get auto loaded. In order to see what modules are loaded, run the following command:

get-module

To see what modules are available which can be loaded run:

get-module -List

You can manually import a module using:

import-module -Name Nameofmodule

Now lets decode a commandlet. The ‘Get’ in get-module is the verb part of it and ‘Module’ is the noun. With so many modules for various applications and numerous commands within each module, to find any particular command can be challenging specially if you don’t have the manual in front of you. Here is how you can do it easily using get-command:

get-command -verb get -noun *

This command with fetch all commands starting with get-

Capture

You can use only verb, only noun or both.

 

 

How to package Integration Packs for Microsoft Orchestrator

When you write your own integration packs for Microsoft Orchestrator, it becomes important to understand how you publish them to Orchestrator in order to be used. The process is simple and is described below:

Build your Visual Studio project for creating the integration pack. Once done you should be able to see a .dll file in the debut folder under bin. This is all we need to import the integration pack into the orchestrator.1

Step 1:

Open the Orchestrator Integration Pack Wizard on your Orchestrator Server.

2

Click on next and enter the details as required. Under Resource File, select the path of the .dll file and click on next.

3

Click on Add to add the Activities.

4

Select the Activities one at a time and click on next when finished.

5

6

Click on next once done. Click next on the screen below.

7

Select the target folder where the output file (.oip) would be stored.

8

Finish the Wizard by clicking next on the remaining screens. The oip file should now be available in the location specified above.

Step 2:

Open Deployment Manager and right click on Integration Packs and select “Register IP with Orchestrator Management Server”. IP Registration Wizard will open.

9

Click on next and select the oip file created in step 1.

10

Click on next and proceed to finish the Wizard.

In the Orchestrator Deployment Manager, Right click on the Integration Packs again and click on Deploy IP to Runbook server.

11

Add the Orchestrator Server where you want to push this and click on next.

12

Proceed to finish the Wizard.

Now open your Orchestrator designer on the server above and you should see your IP under the Activities.

Integrate ServiceNow with Microsoft Orchestrator

ServiceNow(SN) can be integrated with Microsoft Orchestrator using out of the box REST Integration Pack however, it is a very complex process when it comes to read and write data from/to SN. Some paid management packs are also available in the market however if you are planning to build your own to save cost or better control, following information can give you a kick start.

There are two components to make this connectivity, first being authentication and then the queries you want to run. But before that there are some prerequisites that you may need to perform before you can successfully connect to SN:

  1. Make sure the URL for your SN instance (better to use demo instances in the beginning) should be accessible without any proxy
  2. You should have valid admin credentials to the instance
  3. The SN should be set to respond to REST queries and if you are using any ID other than the admin, then that ID should be allowed to fetch data via REST
  4. You should have Visual Studio as well as API for SN and REST
  5. You should have the basic understanding on how to package a management pack and publish it to the Orchestrator

Leave your queries in the comments if you need any further information on these prerequisites. The code given below is just to give you an idea to kick start and by no mean is the best way to code.

Connectivity:

In order to connect to SN and authenticate, use the following code. Once you create the MP using the code below, in Orchestrator, you should see a SN connectivity menu. Type the required details and your settings are now saved and ready to be used in the MP.

using System;
using System.Net;
using System.Net.Mail;
using System.Xml;
using System.Xml.XPath;
namespace Microsoft.SystemCenter.Orchestrator.Integration.Examples.ServiceNow
{
 [ActivityData("ServiceNow Configuration")]
 public class ServiceNowSettings
 {
 private String url = string.Empty;
 private String userName = string.Empty;
 private String password = string.Empty;
 //private string[] test = new string[2000];
 [ActivityInput, ActivityOutput]
 public String URL
 {
 get { return url; }
 set { url = value; }
 }
[ActivityInput, ActivityOutput]
 public String UserName
 {
 get { return userName; }
 set { userName = value; }
 }
[ActivityInput(PasswordProtected = true), ActivityOutput]
 public String Password
 {
 get { return password; }
 set { password = value; }
 }
 [ActivityMethod]
 public string[] Main()
 {
 string UrlRequest = URL+"/api/now/table/sys_db_object?sysparm_limit=10000&sysparm_order_direction=name";
HttpWebRequest request = WebRequest.Create(UrlRequest) as HttpWebRequest;
 request.PreAuthenticate = true;
 request.Credentials = new NetworkCredential(UserName, Password);
 request.Method = "GET";
 request.ContentType = "application/xml";
 request.Accept = "application/xml";
 HttpWebResponse response = request.GetResponse() as HttpWebResponse;
XmlDocument xmlDoc = new XmlDocument();
 xmlDoc.Load(response.GetResponseStream());
 XmlNamespaceManager nsmgr = new XmlNamespaceManager(xmlDoc.NameTable);
 nsmgr.AddNamespace("rest", "http://schemas.microsoft.com/search/local/ws/rest/");
for each location
 XmlNodeList locationElements = xmlDoc.SelectNodes("//result", nsmgr);
int i = 0;
 foreach (XmlNode location in locationElements)
 {
i++;
 }
 string[] arr1 = new string[i];
 int j = 0;
 foreach (XmlNode location in locationElements)
 {
 arr1[j] = location.SelectSingleNode(".//name", nsmgr).InnerText;
 j++;
 }
 Array.Sort(arr1);
 return arr1;} 
 [ActivityOutput]
 public string[] Test
 {
 get { return Main(); }
}}}

Fetching the Data:

For fetching the data, in the same VS project, create another class and use the following code. Once you recreate the MP, you should now see the Fetch Data option under your MP in Orchestrator, drag it to the runbook and use the setting saved, build your query, save the query result to any log file and run the runbook. The results should be visible in the log file.

using System;
using System.Net;
using System.Net.Mail;
using Microsoft.SystemCenter.Orchestrator.Integration;
using System.Xml.XPath;
using System.Xml;
using System.Collections.Generic;
namespace Microsoft.SystemCenter.Orchestrator.Integration.Examples.ServiceNow
{
[Activity("Select Table")]
 public class SelectTable : IActivity
 {
 private ServiceNowSettings settings;
 public string t1;
[ActivityConfiguration]//(Usage = ConfigurationUsage.ExecuteOnly)]
 public ServiceNowSettings Settings
 {
 set { settings = value; }
 get { return settings; }
 }
 public void Design(IActivityDesigner designer)
 {
 designer.AddInput("Select Table").WithListBrowser(settings.Test);
 designer.AddFilter("active");
 designer.AddFilter("assigned_to");
 designer.AddOutput("Selected Table");
}
public void Execute(IActivityRequest request1, IActivityResponse response1)
 {
 string seltable = request1.Inputs["Select Table"].AsString();
 response1.Publish("Selected Table", seltable);
}
}
}

Fetching Incidents:

HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(settings.URL + "/incident.do?WSDL");
 webRequest.PreAuthenticate = true;
 webRequest.Credentials = new NetworkCredential(settings.UserName, settings.Password);
 //webRequest.Timeout = // details elided
 HttpWebResponse webResponse = (HttpWebResponse)webRequest.GetResponse();
 XPathDocument xpathDocument = new XPathDocument(webResponse.GetResponseStream());
 XPathNavigator xpathNavigator = xpathDocument.CreateNavigator();
XmlNamespaceManager xmlNamespaceManager = new XmlNamespaceManager(new NameTable());
 xmlNamespaceManager.AddNamespace("xsd", "http://www.w3.org/2001/XMLSchema");
 int i = 0;
 //foreach (XPathNavigator node in xpathNavigator.Select("//xsd:element/@getKeys", xmlNamespaceManager))
 foreach (XPathNavigator node in xpathNavigator.Select("//xsd:schema/xsd:element[5]/xsd:complexType/xsd:sequence/xsd:element/@name", xmlNamespaceManager))
 {
 i++;
 }
 //Console.ReadLine();//Console.WriteLine("Show all formatted addresses: Option 1");
string[] arr1 = new string[i];
 int j = 0;
 foreach (XPathNavigator node in xpathNavigator.Select("//xsd:schema/xsd:element[5]/xsd:complexType/xsd:sequence/xsd:element/@name", xmlNamespaceManager))
{
 arr1[j] = node.Value;
 j++;
 }
 //Array.Sort(arr1);
 return arr1;}

You can create multiple options in the MP to read, delete, update data in SN. Use a SQL DB to store and manipulate data before uploading to SN again. Your other applications or automation systems can read from this SQL DB and update it once the tasks are completed and upload the results in SN.

Azure Password Writeback with DirSync

Many enterprises deployed Dirsync while integrating with Azure AD to sync the AD users, however, with more and more services moving to cloud and giving SSO solutions with Azure, it becomes a necessity to authenticate cloud applications with on-premise AD password to give the best login experience to the users. There are a couple of options to achieve this.

  1. Synchronize your on-premise AD password with Azure AD. This is a good option however, many enterprises may  not want to opt for it due to various security policies.
  2. Enable Password Writeback to on-premise AD. This is simple to achieve with latest Azure AD Sync tools available from Microsoft however if you have deployed dirsync and cannot upgrade it to the latest tools due to any reason, it becomes tricky.

Microsoft may not have documented it anywhere but here is how this can be achieved in few simple steps:

  1. Enable SSPR(Self Service Password Reset) in Azure (Follow MS documentation on how to do this)
  2. Run Enable-OnlinePasswordWriteback in powershell on your Dirsync server. Follow MS documentation on how to run this
  3. If you have enabled group based SSPR, please ensure the required users are added to it
  4. Assign appropriate license to the users in Azure portal to allow them to reset their passwords
  5. In your on-premise AD, give MSOL_xxxxxxxxxxx AD account delegation to reset user passwords
  6. Register your test user for SSPR and test password reset

Just in case your password reset delegation reverts after sometime, please ensure its excluded from synchronization under Dirsync console.