Showing posts with label Script. Show all posts
Showing posts with label Script. Show all posts

Monday, 29 April 2013

Adding Audit Rules (SACLs) to Active Directory Objects

As part of the work I'm currently doing with Active Directory, my customer requires auditing of user, group and computer object deletions.  The group policy setting "Audit Directory Service Changes" enables the required auditing at an audit policy level, but the system access control lists (SACLs) for the objects don't include deletions by default.  I needed a way to configure all user, group and computer objects in the domain to include such a SACL entry.

Firstly, what are SACLs?  SACLs identify the users and groups that you want to audit when they successfully access or fail to access an object and can be configured not only to include the user or group you are interested in, but also the type of access.  The following screen grab shows the default auditing configured at the root of a new Windows Server 2012 Active Directory domain.


Default auditing on the root of a W2K12 Active Directory



I wanted to configure the SACLs as a task sequence action during the System Center Configuration Manager OSD task sequence I have developed for my customer, so I turned to Windows PowerShell.  After running my script, the auditing configured at the root of the domain looks like this:


Auditing on the root of the domain after the script has run


This is the script I came up with:



#------------------------------------------------------
# | File : ADDSAuditSettings.ps1                                        
# |                                            
# | Purpose : Configures extra auditing (SACLs) at for the
# | new domain
# |
# | Usage : PowerShell.exe -FILE .\ADDSAuditSettings.ps1 
#------------------------------------------------------
# |                                         
# | Author:          JustAnotherTechnicalBlog
# | Creation Date:   26 April 2013
# |
# |
# | Maintenance History                                            
# | ------------------- 
# | 
# | Version:  1.00  2013-04-26  Initial Version  JustAnotherTechnicalBlog
# |
# |
#------------------------------------------------------


# Clear the error variable
#------------------------------------------------------
$error.clear()


# Import the ActiveDirectory PowerShell Module if required
#------------------------------------------------------
if (-not (Get-Module ActiveDirectory))
  {
   Import-Module ActiveDirectory
  }

# This fuction takes a schema GUID ID and a security  
# principal and enables a new SACL entry so deletions
# of target object type by the specified security 
# principal will be audited
#------------------------------------------------------
Function AuditDeletions {

  Param (
         [Parameter(Mandatory=$true)]
         [system.guid]$SchemaIDGUID,
         [Security.Principal.NTAccount]$SecurityPrincipal  
        )

  # Get the DN for the current domain
  #------------------------------------------------------
  $dn = (Get-ADDomain).DistinguishedName

  # Get the current ACLs for the root of the domain
  #------------------------------------------------------
  $acl = Get-ACL -Audit -Path AD:\$dn

  # Build the new SACL rule.
  # This rule will enable auditing of succesful deletion 
  # of our target object.  The rule will be inherited 
  # throughout the domain
  #------------------------------------------------------
  $Rule = New-Object System.DirectoryServices.ActiveDirectoryAuditRule `
                     $SecurityPrincipal, `
                     "DeleteChild", `
                     "Success", `
                     $SchemaIDGUID, `
                     "All"

  # Add the new audit rule to the ACL we 
  # opened earlier
  #------------------------------------------------------
  $acl.AddAuditRule($Rule)

  # Commit the new audit rule
  #------------------------------------------------------
  Set-ACL -Path AD:\$dn -AclObject $acl

}


# To work with AD objects we need the relevent schema
# ID GUIDs. variables to hold these:
#------------------------------------------------------
$ComputerSchemaIDGUID = "bf967a86-0de6-11d0-a285-00aa003049e2"
$GroupSchemaIDGUID    = "bf967a9c-0de6-11d0-a285-00aa003049e2"
$UserSchemaIDGUID     = "bf967aba-0de6-11d0-a285-00aa003049e2"


# The AuditDeletions function above requires a security 
# principal.  user/group we want to audit?
#------------------------------------------------------
$who = "Everyone"


# Put the AuditDeletions function to use ....
#------------------------------------------------------
AuditDeletions $ComputerSchemaIDGUID $who
AuditDeletions $GroupSchemaIDGUID $who
AuditDeletions $UserSchemaIDGUID $who


# Basic error handling
#------------------------------------------------------
If ($error)
  {
   Write-Host "Audit setting configurations failed"
   Exit 1003
  }
Else
  {
   Write-Host "Audit setting configurations completed OK"
  }

Tuesday, 23 April 2013

Automating Active Directory Setup - Install a Child Domain

In previous posts over the past few weeks I've explained how I've installed the Active Directory Domain Services role and provisioned the first domain controller in a root domain using Configuration Manager OSD task sequences and some Windows PowerShell code. The next thing I need to do is install the first domain controller in a subordinate child domain in the new forest. The child domain will hold all my computing resources and users.

The following script:

  • Installs a new domain controller in a child domain, creating that new domain.
  • Gets the variables you will see in the script from Configuration Manager.
  • Creates the new domain within the previously created forest
  • Ensures DNS delegations are created
#-------------------------------------------------------------------
# | File : NewChildDomain.ps1                                           
# |                                            
# | Purpose : Installs the first Domain Controller in a child domain, 
# |           thus creating the new domain
# |           - Designed to be run from a Configuration Manager OSD 
# |             task sequence
# |           - Designed for Windows Server 2012 environments
# |           - Reboot handled by task sequence
# |
# | Usage : PowerShell.exe -FILE .\NewChildDomain.ps1 
#-------------------------------------------------------------------
# |                                         
# | Author:          JustAnotherTechnicalBlog
# | Creation Date:   23 April 2013
# |
# |
# | Maintenance History                                            
# | ------------------- 
# | 
# | Version:  1.00  2013-04-23  Initial Version  JustAnotherTechnicalBlog
# |
# |
#-------------------------------------------------------------------


# Clear the error variable
#-------------------------------------------------------------------
$error.clear()


# Import the ActiveDirectory PowerShell Module if required
#-------------------------------------------------------------------
if (-not (Get-Module ActiveDirectory))
  {
   Import-Module ActiveDirectory
  }


# Here we get access to the Task Sequence variables
#-------------------------------------------------------------------
$objTSenv = New-Object -COMObject Microsoft.SMS.TSEnvironment


# Grab the data we need from the task sequence variables
#-------------------------------------------------------------------
$strTSNetBIOSName =  $objTSenv.Value("RoleVariable1")
$strTSDomainName  =  $objTSenv.Value("RoleVariable2")
$strTSPrntNBName  =  $objTSenv.Value("RoleVariable3")
$strTSPrntDmnName =  $objTSenv.Value("RoleVariable4")
$strTSPrntDmnAcct = "$strTSPrntNBName\" + $objTSenv.Value("RoleAccount2")
$strTSDNSAccount  = "$strTSPrntNBName\" + $objTSenv.Value("RoleAccount3")


# Convert our password to the data type required by Install-ADDSDomain
#-------------------------------------------------------------------
$secstrSafeModePassword = $objTSenv.Value("RoleAccountPassword1") | `
      ConvertTo-SecureString -asPlainText -Force


# Convert our accounts and passwords strings to the data type required
# by Install-ADDSDomain
#-------------------------------------------------------------------
$secstrDomainPassword = $objTSenv.Value("RoleAccountPassword2") | `
     ConvertTo-SecureString -asPlainText -Force
$DomainCreds = New-Object `
     System.Management.Automation.PSCredential("$strTSPrntDmnAcct",$secstrDomainPassword)

$secstrDNSPassword = $objTSenv.Value("RoleAccountPassword3") | `
     ConvertTo-SecureString -asPlainText -Force
$DNSCreds = New-Object `
     System.Management.Automation.PSCredential("$strTSDNSAccount",$secstrDNSPassword)



# Install our first forest Domain Controller, creating a new forest
#-------------------------------------------------------------------
Install-ADDSDomain `
  -Force `
  -NoRebootOnCompletion `
  -CreateDNSDelegation `
  -DomainType Child `
  -DomainMode Win2012 `
  -ParentDomainName "$strTSPrntDmnName" `
  -NewDomainNetBIOSName "$strTSNetBIOSName" `
  -NewDomainName "$strTSDomainName" `
  -SafeModeAdministratorPassword $secstrSafeModePassword `
  -DNSDelegationCredential $DNSCreds `
  -Credential $DomainCreds



# Basic error handling
#-------------------------------------------------------------------
If ($error)
  {
   Write-Host "Child domain creation failed"
   Exit 1001
  }
Else
  {
   Write-Host "Child domain created successfully"



Task Sequence Snippet:  Active Directory Installation

Installing an Active Directory Child Domain with Windows PowerShell

Wednesday, 21 November 2012

Script to fix SCCM Multicast Issue (KB2419357)

I've been working with getting multicast distribution points working with SCCM 2007 R2 recently and one of the challenges is that there is a bug where some WDS settings are not correctly updated on the target server. Microsoft Knowledge Base article KB2419357 documents the issue.

I wrote the following script to update the required settings instead of having to do this manually. The script also makes a change to the ‘ApBlockSize’ setting for WDS, in our environment, where the server is on a 1GB switch port and the clients are on a remote segment on a 100MB switch port, this setting significantly reduced packet fragmentation and increased the speed of the multicast download. I have it in a package and advertise the script to servers hosting multicast enabled distribution points.

# ------------------------------------------------------------------
# Purpose: Applies some registry settings, mostly related to the 
# problem identified in KB2419357. It also makes a change to WDS 
# network settings to improvea packet fragmentation issue identified
# during testing of multicast with SCCM
# 
# Usage : powershell.exe -file .\wdsconfig.ps1
# 
# Version 1.0
# 
# Maintenance History
# ------------------------------------------------------------------
# Version - Date - Change - Author
# ------------------------------------------------------------------
# 1.0 21/11/12 Script Created JustAnotherTechnicalBlog
# 
# ------------------------------------------------------------------

# Set variables
$WDSRegRoot = "HKLM:System\CurrentControlSet\Services\WDSServer"
$WDSService = "Windows Deployment Services Server"


# Write the required SCCM options to the WDS configuration settings
# to the registry
If (Test-Path $WDSRegRoot\Parameters)
{
 Set-ItemProperty -Path $WDSRegRoot\Parameters -Name McStartAddr `
                                               -Value "239.10.10.10"
 Set-ItemProperty -Path $WDSRegRoot\Parameters -Name McEndAddr `
                                               -Value "239.10.10.10"
 Set-ItemProperty -Path $WDSRegRoot\Parameters -Name UdpStartPort `
                                               -Value "63000"
 Set-ItemProperty -Path $WDSRegRoot\Parameters -Name UdpEndPort `
                                               -Value "64000"
}


# Tune the network settings for the WDS server to reduce likelihood
# of packet fragmentation
If (Test-Path $WDSRegRoot\Providers\WDSMC\Protocol)
{
 Set-ItemProperty -Path $WDSRegRoot\Providers\WDSMC\Protocol `
                                               -Name ApBlockSize `
                                               -Value 1385
}


# Restart the Windows Deployment Services Server service
if (Get-Service $WDSService -ea SilentlyContinue)
{
 Restart-Service -displayname $WDSService
}

Tuesday, 26 June 2012

Hide the PowerShell Window

I have a number of scripts I run in the desktop environment that are triggered using scheduled tasks.  These scripts run in the user context.  An unfortunate problem with PowerShell is that it displays a window when it runs, even if it is running as a batch job and no user input or output is involved.  I don't want my end users seeing this window (a black command window), so have tried a number of things and have found two ways to hide it.  Neither of these are ideal, it would be nice to hide this window using PowerShell itself but I haven't found a way to do that.

Method One
This is the method I've used for some time, though I think that the second method below might be the better solution as it uses native tools available in Windows, where this solution requires a third party tool.

Download Hidden Start

Once you have the utility, you can use it like this to hide a running PowerShell script:

%SystemDir%\Scripts\hstart.exe /NOCONSOLE /SILENT "POWERSHELL.EXE -FILE %SystemDir%\Scripts\MyScript.ps1"

Warning:  Some security software suites will detect this tool as 'bad' so if you have problems with it, check you security software logs to see if the utility is being treated as malware.

Update: While writing this post I found the Hidden Start now requires a license for commercial use. I'm pretty sure this was a free tool in the past when I've downloaded it.  This will rule it out for a lot of you I'm sure.

Method Two
This method uses a vbScript to call the PowerShell script as a hidden window. This is not sophisticated but does work. There are a number of ways to do this, but the following little script is quiet cute as you can reuse this code and just pass a new script to it on the command line. The scheduled task would run something like this:

cscript.exe %SystemDrive%\Scripts\RunPowerShellScriptHidden.vbs %SystemDrive%\Scripts\MyScript.ps1

' ----------------------------------------------------------------------

' 
' Purpose:  Prompts end user to contact the Service Desk and opens IE to the
'           Service Desk home page
' 
' Usage : cscript.exe RunPowerShellScriptHidden.vbs mypath\myscript.ps1
' 
' Version 1.0
' 
' Maintenance History
' ----------------------------------------------------------------------
' Version   -    Date      -    Change           -       Author
' ----------------------------------------------------------------------
' 1.0            25/06/12       Script Created           justanothertechnicalblog
' 
' 


Option Explicit

'Variables
Dim oShell, ScriptName, appCmd

' Read target script path from command line argument or echo usage details if arguments are wrong
If WScript.Arguments.Count = 1 Then
ScriptName = WScript.Arguments.Item(0)
Else
Wscript.Echo "Usage: RunPowerShellScriptHidden path\powershellscript"
Wscript.Quit
End If


'Create objects
Set oShell = CreateObject("WScript.Shell")

' Build the command line
appCmd = "PowerShell.exe -file " + ScriptName 

'Run the PowerShell script now
oShell.Run appCmd, 0, false

Thursday, 21 June 2012

Configuration Manager: Pre-stage Distribution Point Data

When I started to write this post I was just going to discuss pre-staging data on distribution points.  I get there in the end, but some comments about PXE Service Points and low latency networks slips in also.  Read on ....

A recent customer of mine had a global network and many of their remote offices had low bandwidth and high latency network connections. Most of the remote offices had a fairly small number of managed clients and only two servers, both functioning as a domain controller and file server with the second server being a hot standby for the first. They used some basics scripts to keep the file server data on the two servers in sync with each other. The customer wanted a flat Configuration Manager hierarchy if possible, though more importantly, didn’t want IIS installed on their domain controllers. That ruled out secondary sites. With this in mind I implemented Branch Distribution Points at these sites and the solution worked well, though we did sometimes have issues where package distributions would stall, so it wasn’t perfect, but for the most part we learnt how to live with that issue and knew how to fix it. We also introduced PXE Service Points at these remote sites but these proved problematic on high latency networks and design work to either script the management of the remote WDS servers outside of Configuration Manager (perhaps using SCCM to run the scripts) or the introduction of Secondary Sites is now being conducted. The following blog article explains the PSP issue exactly as we experienced it. We also opened a support ticket with Microsoft and their advice was the same as this blog suggests – use secondary sites:

Remote site systems and network latency

That above is background. What I wanted to discuss today was actually on the topic of moving package data around networks via a courier instead of over the network. This requirement was important with the above customer because of the network limitations. One advantage of using Branch Distribution Points we found was it is very easy to copy data to the BDP that had been couriered to the remote site. Because of the network limitations, it was often easier, faster and cheaper to courier packages (particularly OSD packages) to these remote sites and copy them manually to the BDP. Microsoft calls this pre-staging packages and documents how to do that. We scripted this process to a degree so it was easy to execute and one of the technical team could take data to a remote site on their laptop or we could courier data on removable media. I can’t share the scripts because they are fairly customer centric, but the manual process works well and is documented below and you should see how this could be scripted with PowerShell and Robocopy quite easily I think:

How to Prestage Packages on a Branch Distribution Point

What if you are using secondary sites (like the above customer is likely to do soon)? Well, there are two options that I’m aware of. I don’t have much experience with either of these but I started doing some reading for my customer above, and have also just started with a new customer were bandwidth isn’t as much of a problem in normal circumstances, but the large amount of data they need to deploy to new sites when they build them is with 40GB+ of data needing to be copied to new secondary site servers.  They are rapidly building new sites as they open new offices or combine offices after a major business merger. I imagine I’ll know more on this topic in the coming weeks but I thought I’d write about what I’ve initially found.

The first option is the inbuilt Courier Sender. I used this back in SMS 2.0 days and it worked but I remember it being labour intensive but don’t really remember why I didn’t like it. The following blog post explains the Courier Sender and the linked document at the end of the post provides a visual guide to using it. I think one issue I had with the Courier Sender was you had to set up the package to use it and that isn’t ideal when you are using the network (Standard Sender) to deliver the package to some DPs and the Courier Sender to others, but I can’t speak from recent experience:

Configuring and using the Courier Sender component in ConfigMgr 2007

The other option, completely new to me but one that looks promising for certain situations, is a Microsoft tool called the ‘Preload Package Tool for Configuration Manager 2007’. It does have limitations. If I’ve understand this tool correctly, it can be used to copy packages to SDPs at child sites. I looks most useful perhaps for preloading packages during initial set up of a new site. It does look like you need to be very aware of your package versions (both the source package versions and the package (.pck file) versions). It also seems to be a tool you can only use once per package per DP. Read the links to the blog posts below that cover the tool in detail.

You can source the tool from here:

Preload Package Tool for Configuration Manager 2007

This blog post gives some guidance and advice about using the tool:

ConfigMgr 2007: The Preload Package Tool (PreloadPkgOnSite.exe) Explained

This blog post also expands on the above one a little:

Packages Will Not Uncompress After Using Preloadpkgonsite.exe

I hope to either update this post or post more on this topic in the future as I get to know these tools better.

Monday, 18 June 2012

Setting Power Management Options on a Network Card

I'm currently working on implementing Wake on LAN (WOL) for a customer's network.  I'll probably post more on what I've found out will researching this in the future.  Today I thought I'd share a script I've created to enable the correct power management options, that are needed on the appropriate network adaptors, to ensure WOL works.
 I wanted a script to ensure the three options in the screen grab below are enabled.  PowerShell is my preferred scripting solution, and this customer also wants as much scripting done in PowerShell as possible.  I found a number of VBScripts examples that I then used to construct my PowerShell script, so I don't take credit for the logic here but I couldn't find a complete PowerShell solution so I hope this is useful for someone.  I'm no PowerShell guru, so if anyone sees ways to improve this script - please feel free to share ideas.


Anyway, here is the screen grab showing the settings I wanted to set:




Here are some links to where I got my logic and ideas from:


ConfigMgr 2007: Implementing Wake-on-LAN (WoL):  This article explains setting up WOL with ConfigMgr very well and includes a VBScript that was my main source for understanding what WMI exposed that I could use for my script


Configure a Network Adapter to Wake a Computer Via PowerShell:  This article was where I found the base I used for my script in PowerShell, it shows how to get at one of the properties via WMI, after figuring this out the rest of the script was easy.


My script follows below.  One thing I've added that is different from the two great articles above is that I look only for physical ethernet adaptors.  I don't try to configure these settings on virtual adaptors or wireless adaptors (others might want to do this for wireless adaptors, Windows 7 does support WOL over wireless, but that isn't a requirement for my customer).  Eliminating virtual adaptors from being configured stops the script touching the various virtual adaptors that various remote access solutions like to add to Windows.


#
# 
# Purpose:  Enables Wake on LAN (WOL) settings on active wired network cards
# 
# Usage : powershell.exe -f .\EnableWOL.ps1
# 
# Version 1.0
# 
# Maintenance History
# ----------------------------------------------------------------------
# Version   -    Date      -    Change           -       Author
# ----------------------------------------------------------------------
# 1.0            18/06/12       Script Created           justanothertechnicalblog
# 
# 

$nics = Get-WmiObject Win32_NetworkAdapter -filter "AdapterTypeID = '0' AND PhysicalAdapter = 'true'"
 
 
foreach ($nic in $nics)
  {

   $nicName = $nic.Name

   Write-Host "--- Enable `"Allow the computer to turn off this device to save power`" on $nicName ---"
   $nicPower = Get-WmiObject MSPower_DeviceEnable -Namespace root\wmi | where {$_.instancename -match [regex]::escape($nic.PNPDeviceID) }
   $nicPower.Enable = $True
   $nicPower.psbase.Put()
  
   Write-Host "--- Enable `"Allow this device to wake the computer`" on $nicName ---"
   $nicPowerWake = Get-WmiObject MSPower_DeviceWakeEnable -Namespace root\wmi | where {$_.instancename -match [regex]::escape($nic.PNPDeviceID) }
   $nicPowerWake.Enable = $True
   $nicPowerWake.psbase.Put()
  
   Write-Host "--- Enable `"Only allow a magic packet to wake the computer`" on $nicName ---"
   $nicMagicPacket = Get-WmiObject MSNdis_DeviceWakeOnMagicPacketOnly -Namespace root\wmi | where {$_.instancename -match [regex]::escape($nic.PNPDeviceID) }
   $nicMagicPacket.EnableWakeOnMagicPacketOnly = $True
   $nicMagicPacket.psbase.Put()
  }



UPDATE 19 June 2012:
Testing today on a laptop I found the above script will identify the inbuilt Intel Centrino Ultimate-N 6300 AGN on a Dell Latitude E6510 as an Ethernet adaptor when it plainly isn't. It then fails to set some of the properties above as they are not supported. Not a big deal, the inbuilt Ethernet adaptor is treated correctly so the result is successful, but messy.  I might have to use the 'netenabled' filter used in the PowerShell Guys' posting above.

UPDATE 06 July 2012:
Here is my updated script to exclude wireless and other cards:



# ---------------------------------------------------------------------- 
#
# Purpose:  Enables Wake on LAN (WOL) settings on active wired network cards
#
# Usage : powershell.exe -f .\EnableWOL.ps1
#
# Version 1.0
#
# Maintenance History
# ----------------------------------------------------------------------
# Version   -    Date      -    Change           -       Author
# ---------------------------------------------------------------------- 
# 1.0            18/06/12       Script Created           justanothertechnicalblog
# 2.0            04/07/12       Updated to exclude       justanothertechnicalblog
#                               Wireless/WiFi/Bluetooth


# Get all physical ethernet adaptors
$nics = Get-WmiObject Win32_NetworkAdapter -filter "AdapterTypeID = '0' `
                                                    AND PhysicalAdapter = 'true' `
                                                    AND NOT Description LIKE '%Centrino%' `
                                                    AND NOT Description LIKE '%wireless%' `
                                                    AND NOT Description LIKE '%WiFi%' `
                                                    AND NOT Description LIKE '%Bluetooth%'"


foreach ($nic in $nics)
  {
  
  $nicName = $nic.Name
  
   Write-Host "--- Enable `"Allow the computer to turn off this device to save power`" on $nicName ---"
   $nicPower = Get-WmiObject MSPower_DeviceEnable -Namespace root\wmi | where {$_.instancename -match [regex]::escape($nic.PNPDeviceID) }
   $nicPower.Enable = $True
   $nicPower.psbase.Put()
   
   Write-Host "--- Enable `"Allow this device to wake the computer`" on $nicName ---"
   $nicPowerWake = Get-WmiObject MSPower_DeviceWakeEnable -Namespace root\wmi | where {$_.instancename -match [regex]::escape($nic.PNPDeviceID) }
   $nicPowerWake.Enable = $True
   $nicPowerWake.psbase.Put()
   
   Write-Host "--- Enable `"Only allow a magic packet to wake the computer`" on $nicName ---"
   $nicMagicPacket = Get-WmiObject MSNdis_DeviceWakeOnMagicPacketOnly -Namespace root\wmi | where {$_.instancename -match [regex]::escape($nic.PNPDeviceID) }
   $nicMagicPacket.EnableWakeOnMagicPacketOnly = $True
   $nicMagicPacket.psbase.Put()
  }