Enclosed a script to join the Azure machine into AD and install the SCCM client. This is useful if you want to populate a native Azure VM that was not installed with SCCM. To make the AD-join a service user was assigned in the script, certainly not the most beautiful variant but this can be encrypted by a compiled EXE. This can be done with the following tool: https://gallery.technet.microsoft.com/scriptcenter/PS2EXE-GUI-Convert-e7cb69d5
Of course, network access to the AD and SCCM server must be available.
#by Jeremias Kühnis
#02.10.2019
### Vars Section ###
$SCCM_Server = "SCCMPrimary.f.q.d.n"
$site_code = "Your Site Code -like S01"
$SCCM_MPServer = "ManagementPoint.f.q.d.n"
$LocalSource_Path = "$env:SystemDrive\temp\"
$SCCM_ClientInstaller = "$LocalSource_Path" + "Client\ccmsetup.exe"
$SCCM_Repo = "\\$SCCM_Server\SMS_***YOURSITECODE***\Client"
$time = ([datetime]::now).tostring("dd_MM_yyyy_HH-mm-ss")
$ScriptFilename = Split-Path $MyInvocation.MyCommand.Definition -leaf
$LogfileName = $LocalSource_Path + 'Azure-SCCMInstaller' + '__' + $time + '.log'
$fqdn = 'someFQDN'
$JoinADUser = $fqdn + '\ServiceAccountjoinAD'
$JoinADUserPw = 'ServiceAccountPW'
$ADOU_NewAzuewDevice = "OU=Germany,DC=contoso,DC=com"
### External Functions ###
#by https://gallery.technet.microsoft.com/scriptcenter/Write-Log-PowerShell-999c32d0
function Write-Log {
[CmdletBinding()]
Param
(
[Parameter(Mandatory = $true,
ValueFromPipelineByPropertyName = $true)]
[ValidateNotNullOrEmpty()]
[Alias("LogContent")]
[string]$Message,
[Parameter(Mandatory = $false)]
[Alias('LogPath')]
[string]$Path = 'C:\Logs\PowerShellLog.log',
[Parameter(Mandatory = $false)]
[ValidateSet("Error", "Warn", "Info")]
[string]$Level = "Info",
[Parameter(Mandatory = $false)]
[switch]$NoClobber
)
Begin {
# Set VerbosePreference to Continue so that verbose messages are displayed.
$VerbosePreference = 'Continue'
}
Process {
# If the file already exists and NoClobber was specified, do not write to the log.
if ((Test-Path $Path) -AND $NoClobber) {
Write-Error "Log file $Path already exists, and you specified NoClobber. Either delete the file or specify a different name."
Return
}
# If attempting to write to a log file in a folder/path that doesn't exist create the file including the path.
elseif (!(Test-Path $Path)) {
Write-Verbose "Creating $Path."
$NewLogFile = New-Item $Path -Force -ItemType File
}
else {
# Nothing to see here yet.
}
# Format Date for our Log File
$FormattedDate = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
# Write message to error, warning, or verbose pipeline and specify $LevelText
switch ($Level) {
'Error' {
Write-Error $Message
$LevelText = 'ERROR:'
}
'Warn' {
Write-Warning $Message
$LevelText = 'WARNING:'
}
'Info' {
Write-Verbose $Message
$LevelText = 'INFO:'
}
}
# Write log entry to $Path
"$FormattedDate $LevelText $Message" | Out-File -FilePath $Path -Append
}
End {
}
}
### own Functions ###
function Mount-SCCMShare {
$DriveName = 'SCCMRepo'
If (!(Get-PSDrive -name $DriveName -ErrorAction SilentlyContinue)) {
try {
New-PSDrive -name $DriveName -PSProvider "FileSystem" -Root $SCCM_Repo -Credential $credential -Scope Script
}
catch {
Write-Log -Path $LogfileName -Level Error -Message "Can not Mount SCCM Share $SCCM_Repo"
Exit
}
}
}
Function SCCMClientInstaller {
###########################################################################
## SCCM Client Health check and Troubleshooting Script
## Author: Lokesh Agarwal
## Date: 23-08-2017
## Input:- SCCM Client path, MP Address, Site Code
## Edited: Jeremias Kühnis 27.09.2019 -> Added/Modifying some Code and add some Log Warning
###########################################################################
############################### Main Code ####################################
$machinename = hostname
############################### Check if WMI is working #######################
if ((Get-WmiObject -Namespace root\ccm -Class SMS_Client -ErrorAction SilentlyContinue) -and (Get-WmiObject -Namespace root\ccm -Class SMS_Client -ErrorAction SilentlyContinue)) {
Write-Log -Path $LogfileName -Level Info -Message "WMI is OK"
$WMI_Status = "Working"
}
else {
Write-Log -Path $LogfileName -Level Warn -Message "Try to Repair WMI"
Stop-Service -Force winmgmt -ErrorAction SilentlyContinue
cd C:\Windows\System32\Wbem\
Remove-item C:\Windows\System32\Wbem\Repository.old -Recurse -Force -ErrorAction SilentlyContinue -Confirm:$false
rename-Item Repository Repository.old -ErrorAction SilentlyContinue -Confirm:$false
Start-Service winmgmt
}
############################# Check if SCCM Client is installed ##################
If (Get-Service -Name CcmExec -ErrorAction SilentlyContinue) {
$Client_Status = "Yes"
Write-Log -Path $LogfileName -Level Info -Message "SCCM Status OK, Client is installed"
########### Check if services are running ################################
$CcmExec_Status = Get-Service -Name CcmExec | % { $_.status }
$BITS_Status = Get-Service -Name BITS | % { $_.status }
$wuauserv_Status = Get-Service -Name wuauserv | % { $_.status }
$Winmgmt_Status = Get-Service -Name Winmgmt | % { $_.status }
$RRegistry_Status = Get-Service -Name RemoteRegistry | % { $_.status }
if ($CcmExec_Status -eq "Stopped") {
Write-Log -Path $LogfileName -Level Warn -message "Try to start Service $CcmExec_Status"
Get-Service -Name CcmExec | Start-Service
}
if ($BITS_Status -eq "Stopped") {
Write-Log -Path $LogfileName -Level Warn -message "Try to start Service $BITS_Status"
Get-Service -Name BITS | Start-Service
}
if ($wuauserv_Status -eq "Stopped") {
Write-Log -Path $LogfileName -Level Warn -message "Try to start Service $wuauserv_Status"
Get-Service -Name wuauserv | Start-Service
}
if ($Winmgmt_Status -eq "Stopped") {
Write-Log -Path $LogfileName -Level Warn -message "Try to start Service $Winmgmt_Status"
Get-Service -Name Winmgmt | Start-Service
}
$MachinePolicyRetrievalEvaluation = "{00000000-0000-0000-0000-000000000021}"
$SoftwareUpdatesScan = "{00000000-0000-0000-0000-000000000113}"
$SoftwareUpdatesDeployment = "{00000000-0000-0000-0000-000000000108}"
#################### check if Scan cycles are working ###################
$machine_status = Invoke-WmiMethod -Namespace root\ccm -Class sms_client -Name TriggerSchedule $MachinePolicyRetrievalEvaluation
$software_status = Invoke-WmiMethod -Namespace root\ccm -Class sms_client -Name TriggerSchedule $SoftwareUpdatesScan
$softwaredeploy_Status = Invoke-WmiMethod -Namespace root\ccm -Class sms_client -Name TriggerSchedule $SoftwareUpdatesDeployment
if ($machine_status -and $software_status -and $softwaredeploy_Status) {
$machine_Rstatus = "Successful"
Write-Log -Path $LogfileName -Level Info -message "Scan cycles are working $machine_Rstatus"
}
else {
$SMSCli = [wmiclass] "root\ccm:sms_client"
Write-Log -Path $LogfileName -Level Warn -message "Scan cycles are not working, try to repair SMSCLI-Client"
$repair = $SMSCli.RepairClient()
}
}
else {
############## Install SCCM Client ###############################
Write-Log -Path $LogfileName -Level Info -message "Missing SCCM Client, try to start installation"
&$SCCM_ClientInstaller /mp:$SCCM_MPServer /logon SMSSITECODE=$site_code
Start-Sleep 5
DO {
$ProcessesFound = (Get-Process -name ccmsetup -ErrorAction SilentlyContinue)
If (($ProcessesFound) -and ($counter -le "90")) {
Start-Sleep 10
$counter++
Write-Host "Still running: $($ProcessesFound) $counter"
}
Else {
IF ($counter -gt "90") {
Write-Log -Path $LogfileName -Level Error -message "Not OK: Try to kill running Process while InstallationProcess is taking more than 15 Minutes"
Get-Process ccmsetup | Stop-Process -Force
Exit
}
Else {
Write-Log -Path $LogfileName -Level Info -message "OK:Process ended by SCCM installer."
}
}
} Until (!$ProcessesFound)
}
####################################################################################################
}
function New-folder {
[CmdletBinding()]
Param(
[Parameter(Mandatory = $true)][string]$folderpath
)
If (!(Test-path $folderpath)) {
New-Item $folderpath -ItemType Directory
}
}
function VerifyPrerequisits {
#Check Powershell Version
$psversion = $PSVersionTable.PSVersion.Major
IF ($psversion -ge "5") {
Write-Log -Path $LogfileName -Level Info -Message "Powershell Version is OK – $psversion"
}
Else {
Write-Log -Path $LogfileName -Level Error -Message "Missing PowerShell or installed Version is to low – $psversion "
$Errorcouonter = "1"
}
#Check .Net FrameWork Version
$DotNetVersion = (Get-ChildItem 'HKLM:\SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full\' | Get-ItemPropertyValue -Name Version)
IF (Get-ChildItem 'HKLM:\SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full\' | Get-ItemPropertyValue -Name Release | Foreach-Object { $_ -ge 461814 }) {
Write-Log -Path $LogfileName -Level Info -Message ".NetVersion ist OK – $DotNetVersion"
}
else {
Write-Log -Path $LogfileName -Level Error -Message "Missing .NetFramework or installed Version is to low – $DotNetVersion "
$Errorcouonter = "1"
}
#Verify SourcePath
IF (!(Test-Path $SCCM_Repo) ) {
Write-Log -Path $LogfileName -Level Error -Message "Can not find SCCM installer Path: $SCCM_Repo"
$Errorcouonter = "1"
}
##Last Part
If ($Errorcouonter -eq "1") {
Write-Log -Path $LogfileName -Level Warn -Message "Installation is not performed because the checked prerequisites are Wrong/Missing. See further up in the log."
exit
}
Else {
Write-Log -Path $LogfileName -Level Info -Message "Prerequisits are Okay, try to lead trough installation"
}
}
Function Remove-InstallerLocation {
$removepaths = @(
"$LocalSource_Path\Client"
)
foreach ($ToDelPath in $removepaths) {
Remove-Item $ToDelPath -Recurse -Force -ErrorAction SilentlyContinue
}
}
###################
### Main Script ###
###################
$password = $JoinADUserPw | ConvertTo-SecureString -asPlainText -Force
$credential = New-Object System.Management.Automation.PSCredential($JoinADUser, $password)
### Create C:\Temp Folder, if not Exists for logs ###
New-folder -folderpath $LocalSource_Path
### Join AD-Section ###
#Need to Add credentials and start Job for OU path
$Error.Clear()
Try {
Add-Computer -DomainName $fqdn -Credential $credential -OUPath $ADOU_NewAzuewDevice -Force -ErrorAction Stop
}
catch [System.Net.WebException], [System.Exception] {
Write-Log -Path $LogfileName -Level Error -Message "Failed @ Ad-join Part...maybe ADAccount already exists."
Write-Log -Path $LogfileName -Level Error -Message $Error
Exit
}
Mount-SCCMShare
VerifyPrerequisits
### Install SCCM-Client Section ###
Write-Log -Path $LogfileName -Level Info -message "Start-SCCM Repair/ Install Process..."
Copy-Item $SCCM_Repo -Recurse -Destination $LocalSource_Path -Force
SCCMClientInstaller
Write-Log -Path $LogfileName -Level Info -message "End Section: 'Start-SCCM Repair/ Install Process...'"
Write-Log -Path $LogfileName -Level Info -message "Finished all Jobs, if there are any Problems Check if Computer is joined in AD and Check the Execution Policy. Also Check de Install status of SCCM Client."
Write-Log -Path $LogfileName -Level Info -message "Try to clean Up local install Files"
Start-Sleep 3
Remove-InstallerLocation
Write-Log -Path $LogfileName -Level Info -message "Successfully finish..."
#reboot while AD-Join
shutdown -r -f -t 60