Skip navigation

Category Archives: Work

So this is a quick blog for a frustrating issue, that is actually very easy to resolve.

If you are on Windows Server 2012, and you’ve had to reinstall WSUS for any reason and receive the following error in your tmp log after attempting to finalize the installation:

2013-02-20 16:04:17  Creating default subscription.

2013-02-20 16:04:17  Instantiating UpdateServer

2013-02-20 16:04:19  CreateDefaultSubscription failed. Exception: System.Net.WebException: The request failed with HTTP status 503: Service Unavailable.

   at System.Web.Services.Protocols.SoapHttpClientProtocol.ReadResponse(SoapClientMessage message, WebResponse response, Stream responseStream, Boolean asyncCall)

   at System.Web.Services.Protocols.SoapHttpClientProtocol.Invoke(String methodName, Object[] parameters)

   at Microsoft.UpdateServices.Internal.ApiRemoting.GetServerVersion()

   at Microsoft.UpdateServices.Internal.DatabaseAccess.AdminDataAccessProxy.GetServerVersion()

   at Microsoft.UpdateServices.Internal.BaseApi.UpdateServer.CreateUpdateServer(String serverName, Boolean useSecureConnection, Int32 portNumber)

   at Microsoft.UpdateServices.Internal.BaseApi.UpdateServer..ctor(Boolean bypassApiRemoting)

   at Microsoft.UpdateServices.Setup.StartServer.StartServer.CreateDefaultSubscription()

2013-02-20 16:04:19  StartServer encountered errors. Exception=The request failed with HTTP status 503: Service Unavailable.

2013-02-20 16:04:19  Microsoft.UpdateServices.Administration.CommandException: Failed to start and configure the WSUS service

   at Microsoft.UpdateServices.Administration.PostInstall.Run()

   at Microsoft.UpdateServices.Administration.PostInstall.Execute(String[] arguments)

It’s most likely an IIS issue.

Open the IIS console, delete the WSUS Site, and perform the post installation tasks again.

Something like that anyway….

 

I plan on posting more stuff in the future after things have normalized.

Soooo……

After GatherWriterMetadata SMS Writer status = FAILED_AT_PREPARE_BACKUP. SMS_SITE_BACKUP 2/4/2013 3:32:38 PM 8500 (0x2134)
Error: VSS_E_WRITERERROR_TIMEOUT. Error Code = 0x80,042,3f2. SMS_SITE_BACKUP 2/4/2013 3:32:38 PM 8500 (0x2134)

vssadmin list writers

Capture

hmmmm, it’s there.  Let me check the sms_site_sql_backup service on the database servers.

Ok, lets check our permissions on the servers to make sure machines have local admin, share permissions, and that they have SA rights.

Hmmm, are the backup components installed on the SQL nodes?

They are,  sorta, but aren’t installed on the static drives of our SQL server nodes, this is a problem…

 


It all starts with our old friend NO_SMS_ON_DRIVE.SMS

 

So make sure it’s where it needs to be, like on SAN drives.  Especially a SANS drive tied to SQL Cluster that will be failing over.  You don’t want to manually re-point components; but in case you do (or in our case NEED to)…. here’s how:

Get on every drive that DOESN’T need sms site components installed to them and place NO_SMS_ON_DRIVE.SMS in their root.

Now open your registry on the site server and go to:

hklm\Software\Microsoft\SMS\Components\SMS_SITE_COMPONENT_MANAGER\Multisite Component Servers\<servername>\Installation Directory

Change this path to the preferred static drive on the respective servers.  Now:

net stop sms_site_component_manager
net stop sms_executive
net start sms_site_component_manager

Now check sitecomp.log for your server name, verify the installation and connection.  Now lets check the local drives of the component sql server and verify the installation path we declared earlier.

Is it there?  If so, great, if not….

Open services and look for:

SMS_SITE_SQL_BACKUP_<site servername>

Go to properties and check it’s local path.

Stop the SMS_SITE_SQL_BACKUP_<site servername> service

Now open the registry and go to

hklm\System\CurrentControlSet\services\SMS_SITE_SQL_BACKUP_*\ImagePath

Specify the local drive and path you wish to use and now move the contents from the previous service to it’s new home.

You’ll need to verify and do the same for the log files:

hklm\Software\Microsoft\SMS\Tracing\SMS_SITE_SQL_BACKUP<site servername>\TraceFilename

Now:

Start the SMS_SITE_SQL_BACKUP_<site servername> service

Rinse, and repeat until they are where they need to be, and finally perform and verify the backup completes:

(from the site server)

net start sms_site_backup

and watch the smsbkup.log


Props to MS PFE Sean MAHONEEEEEEY! For his assistance

I’ve updated the inventory enforcement script and post for anyone who utilizes it. It should be cleaner now as it only depends on wmi for the inventory actions.

Here’s the link back.

So in an attempt to quickly extract OS version and Service Pack for a few machines in an environment the idea was presented to pull the data from active directory. The properties exist so the logic seemed sound; and as we’ve discussed this before it’s a pretty easy task with the active directory module in PowerShell, and here’s the code:

$list = gc computers.txt
Import-Module ActiveDirectory
ForEach($item in $list){
                $ado = (get-adcomputer $item -Properties *)
                $pso = New-Object PSObject
$pso | Add-Member -Name "Computer Name" -MemberType NoteProperty `
		-Value $ado.CN
$pso | Add-Member -Name "Operating System" -MemberType NoteProperty `
		-Value $ado.OperatingSystem
$pso | Add-Member -Name "Service Pack" -MemberType NoteProperty `
		-Value $ado.OperatingSystemServicePack
$pso
}

Assuming for the sake of example the name of this script is, get-adservicepack.ps1, and you’ve got your computers.txt file with your computer names in it then we’d run it like this.

./get-adservicepack.ps1 | export-csv -NoTypeInformation MyAdOutput.csv

So what’s happening?

First, we’re taking the get-content command to pull data from a local text file “computers.txt” into a data object and then iterating through it sequentially.  We are then using the computer name as the lookup name with the get-adcomputer cmdlet along with all it’s ad properties and assign it to a variable called ado.

Now we create a PowerShell object and begin to give it some noteproperties with values pulled from our ad object we created from the ad cmdlet then echo it’s contents out by calling it.

When we run the script and pipe it’s output to export-csv –NoTypeInformation we are taking that output and putting it directly into a csv without any of the object information, otherwise it’s a tabled console output.

PowerShell is so boss sometimes…

Maybe we just do all this in one line?

gc computers.txt|ForEach-object{Get-ADComputer $_ -properties *|select -Property name,operatingsystem,operatingsystemservicepack}|export-csv -notypeinformation output.csv

Scroll that line, like a boss.

Gallery entry on Script Center if you want to rate it

The 9 traits of a veteran Unix Admin:

  1. Bold
  2. Pretentious
  3. Obfuscated
  4. Lazy x4
  5. Minimalist
  6. Condescending
  7. Inquisitive
  8. Exclusive
  9. Unyielding

 

Now the original article from 2011 I read for this made those traits sound a lot better, but ultimately that’s about right.

These aren’t inherently bad traits for a systems admin (some are though), but they obviously require some polish before being acceptable in the workplace, but I thought this was worth sharing with the community at large.

 

So I haven’t written anything even mildly technical lately so I thought I’d show a small script I use to copy and restore my scripts and PowerShell profile across multiple machines.  It’s fairly straight forward.  It will backup my scripts directory from my desktop and PowerShell folder from my documents directory; and recreates the the folder structure in the directory the script is run from.

Conversely it will restore as well provided the content exists.  It is USER specific, so if you use multiple user id’s like I do, it’s important that you manage them accordingly.


Option Explicit
On Error Resume Next
Dim oWShell, oFSO, oNet
Dim strCurUser, strSourceDrive, strTargetDrive, strOption, strMsg
Set oWShell = CreateObject("Wscript.Shell")
Set oFSO 	= CreateObject("Scripting.Filesystemobject")
Set oNet 	= CreateObject("Wscript.Network")

strCurUser = oNet.UserName
strTargetDrive = Left(WScript.ScriptFullName, Len(WScript.ScriptFullName)- _
				Len(WScript.ScriptName)) & strCurUser
strSourceDrive = oWShell.ExpandEnvironmentStrings("%userprofile%")
strMsg = "Filecopy Summary: "& vbcrlf
strOption = LCase(InputBox("Restore|Backup"))

Select Case strOption
    Case "restore"	Call Restore()
    Case Else	Call Backup()
End Select

WScript.Echo strMsg
'-------------------
Function Backup()
	
Dim strScripts, strPShell
	strScripts = "\Desktop\Scripts" : strPShell = "\Documents\windowspowershell"

If Not CreateFolder(strTargetDrive & strScripts) Then
	strMsg = strMsg & "Failed Creating: "& strTargetDrive & strScripts & vbCrLf
Else
	strMsg = strMsg & "Created: " & strTargetDrive & strScripts & vbCrlf
End If

If Not CreateFolder(strTargetDrive & strPShell) Then
	strMsg = strMsg & "Failed Creating: "& strTargetDrive & strPShell & vbCrLf
Else
	strMsg = strMsg & "Created: " & strTargetDrive & strPShell & vbCrlf
End If
	
If Not CopyFolder(strSourceDrive & strScripts, strTargetDrive & strScripts) Then
	strMsg = strMsg & "Failed to Copy: " &  strSourceDrive & strScripts & vbCrLf
Else
	strMsg = strMsg & "Copied: " & strSourceDrive & strScripts & vbCrLf
End If

If Not CopyFolder(strSourceDrive & strPShell, strTargetDrive & strPShell) Then
	strMsg = strMsg & "Failed to Copy: " & strSourceDrive & strPShell & vbCrLf
Else
	strMsg = strMsg & "Copied: " & strSourceDrive & strPShell & vbCrLf
End If

End Function
'-------------------
Function Restore()

Dim strScripts, strPShell, temp1, temp2
	strScripts = "\Desktop\Scripts" : strPShell = "\Documents\windowspowershell"
temp1 = strSourceDrive : temp2 = strTargetDrive
strTargetDrive = temp1 : strSourceDrive = temp2

If Not CreateFolder(strTargetDrive & strScripts) Then
	strMsg = strMsg & "Failed Creating: "& strTargetDrive & strScripts & vbCrLf
Else
	strMsg = strMsg & "Created: " & strTargetDrive & strScripts & vbCrlf
End If

If Not CreateFolder(strTargetDrive & strPShell) Then
	strMsg = strMsg & "Failed Creating: "& strTargetDrive & strPShell & vbCrLf
Else
	strMsg = strMsg & "Created: " & strTargetDrive & strPShell & vbCrlf
End If
	
If Not CopyFolder(strSourceDrive & strScripts, strTargetDrive & strScripts) Then
	strMsg = strMsg & "Failed to Copy: " &  strSourceDrive & strScripts & vbCrLf
Else
	strMsg = strMsg & "Copied: " & strSourceDrive & strScripts & vbCrLf
End If

If Not CopyFolder(strSourceDrive & strPShell, strTargetDrive & strPShell) Then
	strMsg = strMsg & "Failed to Copy: " & strSourceDrive & strPShell & vbCrLf
Else
	strMsg = strMsg & "Copied: " & strSourceDrive & strPShell & vbCrLf
End If

End Function
'-----------
Function CreateFolder(strFolder)
On Error Resume Next
	Dim FolderArray, Folder, Path, booErr
	FolderArray = Split(strFolder,"\")
		For Each Folder In FolderArray
			If Path <> "" Then
				If Not oFSO.FolderExists(Path) Then
					oFSO.CreateFolder(Path)
				End If
			End If
				Path = Path & LCase(Folder) & "\"
			If Err.Number <> 0 Then
				booErr = True
			End If
		Next
		If booErr Then
			CreateFolder = False
		Else
			CreateFolder = True
		End If
End Function
'-----------
Function CopyFolder(strSource, strDest)
On Error Resume Next
	If oFSO.FolderExists(strDest) Then
		oFSO.DeleteFolder strDest, True
	End If
		oFSO.CopyFolder strSource, strDest, True

	If Err.Number <> 0 Then
		CopyFolder = False
	Else
		CopyFolder = True
	End If
End Function

There you go, a bit lacking on the sophistication side of things, but it gets the job done for me.  Feel free to modify the script for your own personal file backup needs, it can really help to streamline the process.

So I hit up a friend of mine at work to write a quick write up and example of batch scripting; which he leverages heavily administratively, and here’s the result:

For me, batch is the quickest and easiest way to automate most tasks for server administration.  These tasks can be completed by leveraging built-in commands, resource kit executables, and other Microsoft/SysInternals type tools.  I would like to begin at the beginning, but if I had that much patience I would have learned a real scripting language, so I will start in the middle.

Step 1 is figuring out how to get information you need to manipulate out of the command line, here’s a good start:

http://technet.microsoft.com/en-us/library/cc778084(v=ws.10)
http://www.microsoft.com/en-us/download/details.aspx?id=17657
http://technet.microsoft.com/en-us/sysinternals/bb842062.aspx

Step 2 is formatting and passing that information to additional tools and/or parsing the results for review, (done by the batch itself).

Scenario:  You have a directory on a server containing many user profiles.  Under these profile directories is a ‘cache’ directory that you want to erase every night.  User profiles are create/deleted on a regular basis, so creating a static batch of RMDIR commands is not an option.

C:\Users>dir
Volume in drive C has no label.
Volume Serial Number is B043-A733

Directory of C:\Users

07/03/2012  07:20 AM    <DIR>          .
07/03/2012  07:20 AM    <DIR>          ..
07/03/2012  07:17 AM    <DIR>          Andy
07/03/2012  07:17 AM    <DIR>          Curtis
07/03/2012  07:17 AM    <DIR>          Daniel
07/03/2012  07:17 AM    <DIR>          Mark

You want to run the command: rmdir /s /q c:\users\<insert username>\cache, where the username is dynamically pulled from the current c:\users directory.  First, tweak your command with any available options/flags that give you an output that is easier to work with.  In this case, the /b (brief) flag for DIR will strip the output of any extra format.

C:\>dir c:\users /b
Andy
Curtis
Daniel
Mark

We will reference the output from this command as a single string variable per line, so removing header formatting and unnecessary columns is ideal.   For this script I’ll be using a FOR loop; there are several different types of FOR loops in DOS, though I use /F (filenameset) almost exclusively.  Typing FOR /? from a command prompt will give you the full list of options, but we will focus on the two that read from either a text file or a command output.  From the FOR /? help:

FOR /F [“options”] %variable IN (file-set) DO command [command-parameters]
FOR /F [“options”] %variable IN (‘command’) DO command [command-parameters]

The “options” I most commonly use are “tokens” and “delims”, to specify which column(s) to read from, and what character(s) delimit columns (blank space is the default delimiter).  We narrowed down the output to only one column, so “delims” will not be used in this example, and there is only one token available.

@echo off 
for /f “tokens=1” %%a in (‘dir c:\users /b’) do (rmdir /s /q c:\users\%%a\cache)

 

Lets take a closer look at each component of the for loop and what it is doing:

for /f

Perform an action for every line in a text output

“tokens=1”

Identify the first word on every line (tokens=2 would be the second word, etc)

%%a

Reference that first word of every line as %%a

in (‘dir c:\users /b’)

The text output we are reading from is the command ‘dir c:\users /b’

you can also read a text file instead of command output by removing the single quotes,

IE: for /f “tokens=1” %%a in (serverlist.txt)

do (actions)

action(s) to be performed for every line

Now this batch is ready to be added as a scheduled task and automatically delete the specified cache directories every night.  You can create the scheduled task via command line as well, but I don’t see much of a need for that in this case. As much as I enjoy automation and CLI, I typically avoid both when they are not the quickest way to get the job done.

Alright, so if you read my previous post for AD to DB then this post will make more sense.  However if you haven’t; then now would be a good time to… go ahead, I’ll wait.

Now one of the primary purposes I had for this data was to leverage it against my v_r_system view and determine which active assets are missing their client.  Well that’s fine and good, but I had been in the habit of taking that data and then performing a DNS check for the entries using a Powershell script written by Jason Sandys.  Originally I was using a vbscript I wrote to do so, but found his to be far more efficient since it made use for the DNS class in .Net.  But I digress, the end result is that I had quite a few additional steps to determine which machines were active, and ready for remediation either locally by desktop support, or by my remote repair tools.

So, what to do?  Why not query what I need, run the DNS check from a data set, then write the results back to my data warehouse?  This script requires a few things:

  1. Working, integrated credential for querying the SCCM DB; and permission to drop, insert, and create for the DB warehouse.
  2. Table within the data warehouse with the AD data provided with my AD to DB script to join against the SCCM DB data.
  3. Established linked servers between the two databases to perform the join (stored query in the script calls the join from the SCCM DB server, so the link is required there)

 

Now that we have that out of the way lets discuss the variables that need to be modified here.

  • $db – SCCM Database for the primary site server
  • $sqlsrvr – SCCM Database Server Name
  • $db2 – Data Warehouse DB
  • $sqlsrvr2 – Data Warehouse Server Name
  • $table – Data Warehouse AD Table

 

Alright, so there it is, now time for the code; which I want to apologize in advance for it’s wide column width (download):

#We expect errors for hosts we can't find, so running silently
	$ErrorActionPreference = "SilentlyContinue"
#Configuring connection and query variables for the sql client adapter
$db = "sms_abc" 					#sccm database
$sqlsrvr = "SCCMDBServer"  			#sccm db Server Name
$db2 = "DataWarehouse"				#Data Warehouse db
$sqlsrvr2 = "DataWarehouseServer"	#Data Warehouse Server Name
$table = "ADtablefromDatawarehouse"	#The table where AD data is stored
$sqlquery = "select sys.Name0 from $sqlsrvr.$db.dbo.v_r_system as sys join `
			$sqlsrvr2.$db2.dbo.$table as adlist on adlist.ad_machine = sys.name0`
			where DATEDIFF(d,passwordlastset,getdate()) <= 30 and Client0 = 0 or`
			DATEDIFF(d,passwordlastset,getdate()) <= 30 and Client0 is null"
#Performing the query and writing to a data set
$sqlcon = New-Object System.Data.SqlClient.SqlConnection("Data Source=$sqlsrvr;Integrated Security=SSPI;Initial Catalog=$db;")
	$cmd = New-Object System.Data.SqlClient.SqlCommand
		$cmd.CommandText = $SQLQUERY
			$cmd.Connection = $SQLCON
	$sqladapter = New-Object System.Data.SqlClient.SqlDataAdapter
		$sqladapter.SelectCommand = $CMD
			$DS = New-Object System.Data.DataSet; $DS.Tables.Add("SQLQuery")
	[Void]$sqladapter.Fill($DS.Tables["SQLQuery"])
$sqlcon.Close()
#Building new table in SQL for DNSQuery
$table = "NoClientDNSRecord"	
	$sqlcon = New-Object System.Data.SqlClient.SqlConnection("Data Source=$sqlsrvr2;Integrated Security=SSPI;Initial Catalog=$db2;")
$sqlcon.Open()
	$cmd = $sqlcon.CreateCommand()
		$cmd.CommandText = "drop table $table"
			[Void]$cmd.ExecuteNonQuery() 
		$cmd.CommandText = "create table $table (Name varchar(150) not null Primary key,IP varchar(25), Reverse varchar(150), status varchar(50))"
			[Void]$cmd.ExecuteNonQuery() 
#Performing a DNS query against each machine in our SQL data set
foreach($row in $DS.Tables["SQLQuery"].rows){
	$system = $row[0]
	$sys = New-Object PSObject
		$sysname = $system.ToLower().Trim()
			$sys | Add-Member -MemberType NoteProperty -Name Name -Value $sysname
	#Getting IP address for the host name
	$sys | Add-Member -MemberType NoteProperty -Name IP -Value "-"
		$sys.IP = [System.Net.DNS]::GetHostEntry($sysname).AddressList | select -First 1
			$firstOctet = ($sys.IP -split "[.]")[0].Trim()
	#Getting reverse address from dns for the host name
	$sys | Add-Member -MemberType NoteProperty -Name Reverse -Value "-"
		$sys.Reverse = [System.Net.DNS]::GetHostEntry($sys.IP).HostName | select -First 1
			$sys.Reverse = ($sys.Reverse -split "[.]")[0].ToLower().Trim()
				if ($sys.Reverse -eq $firstOctet){$sys.Reverse = "-"}
	#Writing a status for the entry based on  name and reverse lookups.	
	$sys | Add-Member -MemberType NoteProperty -Name Status -Value "-"
		if		($sys.IP -eq "-")			`
		{$sys.Status = "Could not Resolve IP"}
		elseif	($sys.Reverse -eq "-")		`
		{$sys.Status = "IP Address not found in reverse zone"}
		elseif	($sys.Name -ne $sys.Reverse)`
		{$sys.Status = "IP registered to another system"}
		else								`
		{$sys.Status = "OK"}
#Writing values to SQL
$cmd.CommandText = "insert $table values ('$($sys.name)','$($sys.ip)','$($sys.reverse)','$($sys.status)')"; [Void]$cmd.ExecuteNonQuery()}
$sqlcon.close()

 

I’d recommend using PowerGUI for reviewing/modifying this code as it’s by far the best (free) powershell editor available.

Until next time, have a good one!