I have the script below and for the life of me can not get why it is giving me "You cannot call a method on a null-valued expression." It errors on two spots.
Which computer?: NFDW2206
What is the AssetID?: 00000007
Checking NFDW2206 to see if the Registry Key exists..
You cannot call a method on a null-valued expression.
At \\NFDNT007\Dept\Corporate\IT\Network Services\Documentation\Asset Tag.ps1:11 char:33
+ $regassetid = $regKey.GetValue <<<< ("AssetID")
+ CategoryInfo : InvalidOperation: (GetValue:String) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull
The Key does not exist. Writing AssetID.....
You cannot call a method on a null-valued expression.
At \\NFDNT007\Dept\Corporate\IT\Network Services\Documentation\Asset Tag.ps1:18 char:20
+ $regKey.Setvalue <<<< ('AssetID', $AssetID, 'String')
+ CategoryInfo : InvalidOperation: (Setvalue:String) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull
The code is below
$Computer = Read-Host "Which computer?"
$AssetID = Read-Host "What is the AssetID?"
if (($Computer -eq "") -or ($AssetID -eq "")) {
Write-Host "Error: A blank parameter was detected" -BackgroundColor Black -ForegroundColor Yellow
} else {
if (Test-Connection -comp $Computer -count 1 -quiet) {
Write-Host "Checking $Computer to see if the Registry Key exists.."
$reg = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey("LocalMachine", $Computer)
$regKey = $reg.OpenSubKey("SOFTWARE\Multek Northfield")
$regassetid = $regKey.GetValue("AssetID")
if ($regassetid -eq $null) {
Write-Host "The Key does not exist. Writing AssetID....."
$reg = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey("LocalMachine", $Computer)
$regKey = $reg.OpenSubKey("SOFTWARE\Multek Northfield",$True) ## $True = Write
$regKey.Setvalue('AssetID', $AssetID, 'String')
} else {
$OverWrite = Read-Host "AssetID exists do you wish to continue?"
if (($OverWrite -eq "y") -or ($OverWrite -eq "yes")) {
$reg = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey('LocalMachine', $Computer)
$regKey = $reg.OpenSubKey("SOFTWARE\Multek Northfield",$True) ## $True = Write
$regKey.Setvalue("AssetID", $AssetID, "String")
}
}
} else {
Write-Host "Error: $computer is offline..." -BackgroundColor Black -ForegroundColor Yellow
}
}
The docs say that OpenSubKey will return null if the operation fails. It is likely it can't find the key. Is the remote system a 64-bit OS? If so, it could be you're running into a registry virtualization issue. Might need to look under SOFTWARE\WOW6432Node\Multek Northfield
Related
I have a PS Script that checks the DNS for our domain server.
The Powershell script runs every 15 minutes and usually is successful. However, occasional it fails at random with the error below but there is nothing consistent about it when it fails.
Exception calling "Open" with "0" argument(s): "A connection was successfully established with the server, but then an
error occurred during the login process. (provider: SSL Provider, error: 0 - An existing connection was forcibly closed
by the remote host.)"
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : SqlException
If I can't find the cause, I was wondering if there is a way to check for this error and automatically re-run the script?
Thanks in advance.
UPDATE: Code Below
Try
{
# Query DNS server, loop through each domain
Invoke-Command -ComputerName $dc -ScriptBlock `
{
Param($domains, $dnsserver)
$ConnectionString = 'Data Source=MYDB; Initial Catalog=MYCAT; Integrated Security=SECURE'
$Conn = New-Object System.Data.SqlClient.SqlConnection($ConnectionString)
$Conn.Open()
Foreach ($domain in $domains)
{
# Query specific record types
$results = (Resolve-DnsName $domain -Server $dnsserver -Type NS)
$results | Foreach-Object `
{
$name = $_.name
$type = $_.type
$value = $_.NameHost
$Sql = "INSERT INTO B5_DNSCheck([DOMAIN], [TYPE], [VALUE]) VALUES('$domain','$type','$value')"
$Cmd = $Conn.CreateCommand()
$Cmd.CommandText = $Sql
$Cmd.ExecuteNonQuery() | Out-Null
}
$results = (Resolve-DnsName $domain -Server $dnsserver -Type MX)
$results | Foreach-Object `
{
$name = $_.name
$type = $_.type
$value = $_.NameExchange
$Sql = "INSERT INTO B5_DNSCheck([DOMAIN], [TYPE], [VALUE]) VALUES('$domain','$type','$value')"
$Cmd = $Conn.CreateCommand()
$Cmd.CommandText = $Sql
$Cmd.ExecuteNonQuery() | Out-Null
}
}
} -ArgumentList $domains, $dnsserver
Write-Output "Completed DNS check"
}
catch
{
Write-Output "Error with Connection"
Stop-Transcript
Exit 2
}
}
Else
{
Write-Output "Error: Unable to conect to $dnsserver to check DNS at this time"
Stop-Transcript
Exit 2
}
# All done
Stop-Transcript
The answer was #TheMadTechnicians advice.
Adding in $ErrorActionPreference='Stop' and a try/catch worked perfect.
So I have a list of sitecollections and with them everyone who have access to the sitecollections. A ; separated csv file, formated like /sites1/sites;AD\User1.
We need to make a cleanup on ~400 sites where we want to remove users beloning to a certain company or being in a certain AD.
I have this information so thats not a problem.
Is there a way to make a powershellscript to remove theese users on theese site collection, in the other output we don't have any list of where they have permission only that they have it somewhere on the site.
I would also need the output format in a good way so we can restore them in a case it would be needed?
Can this be done?
We used below to get the list of users:
$ver = $host | select version
if($Ver.version.major -gt 1) {$Host.Runspace.ThreadOptions = "ReuseThread"}
if(!(Get-PSSnapin Microsoft.SharePoint.PowerShell -ea 0))
{
Add-PSSnapin Microsoft.SharePoint.PowerShell
}
$pathSave = "D:\Script\Output.csv"
$pathRead = "D:\Script\\sites.txt"
write-output = "======================================================================="
[System.Collections.ArrayList]$objectCollection = #();
[System.Collections.ArrayList]$allUsersArray = #();
foreach($line in Get-Content $pathRead)
{
$site = Get-SPSite($line)
$sitecntr = 0;
#write-output " ";
write-output $site.Url;
$allusers = $site.RootWeb.AllUsers;
foreach ($user in $allusers)
{
if (!$user.IsDomainGroup)
{
$sitecntr++;
$outstring = $site.Url + ";" + $user.UserLogin + ";" + $user.Email
$a = $allUsersArray.Add($outstring);
}
}
$outString = "Totalt: " + $sitecntr
if ($sitecntr -ne 0) {
write-output $outstring;
write-output " ";
}
$site.Dispose();
}
write-output ""
write-output "Exporting to csv..."
Out-File -FilePath $pathSave -InputObject $allUsersArray
write-output "Finished! "
write-output ""
This script will ensure reader permissions to the specified user across the site collection.
**Keep in mind:** This is not the right way to deal with permissions issues!
$site = Get-SPSite -Identity "http://mysite/"
$user = Get-SPUser -Identity "mydomain\myuser" -Web $site.RootWeb
$assignment = New-Object Microsoft.SharePoint.SPRoleAssignment($user)
$role = $site.RootWeb.RoleDefinitions[[Microsoft.SharePoint.SPRoleType]::Reader]
$assignment.RoleDefinitionBindings.Add($role);
foreach ($web in $site.AllWebs) {
if ($web.HasUniquePerm) {
$web.RoleAssignments.Add($assignment)
}
}
Note: You need to be Site Collection Admin to use this script.
I’m writing some code to compare installed versions of software in some test computers currently I’m using a PowerShell PS1 script to create a text file and compare with previously created baseline text file
For the ease of end users I would like to automate these in a excel file, press a button and you get a result of what does not match with the baseline
My current ps1 scripts are
:CreateDefaultApps.ps1 - Create the Default baseline DefApp32.txt or DefApp64.txt in My Documents folder
cls
$CurDir = $myinvocation.InvocationName | split-path -parent
$DOCDIR = [Environment]::GetFolderPath("MyDocuments")
$TARGETDIR = "$DOCDIR\AppLog"
$COMPNAME = $env:computername
if(!(Test-Path -Path $TARGETDIR ))
{
New-Item -ItemType directory -Path $TARGETDIR
}
cls
if ([Environment]::Is64BitOperatingSystem)
{
Write-Host "64bit Windows Detected"
Write-Host "Collecting Product Information for"$COMPNAME
If (Test-Path $TARGETDIR\DefApp64.txt)
{
Remove-Item $TARGETDIR\DefApp64.txt
}
Else
{
Get-WmiObject -Class Win32_Product | Select-Object -Property Name, Version > $TARGETDIR\DefApp64.txt
}
write-host "File"$TARGETDIR"\DefApp64.txt Created"
}
else
{
Write-Host "32bit Windows Detected"
Write-Host "Collecting Product Information for"$COMPNAME
If (Test-Path $TARGETDIR\DefApp32.txt)
{
Remove-Item $TARGETDIR\DefApp32.txt
}
Else
{
Get-WmiObject -Class Win32_Product | Select-Object -Property Name, Version > $TARGETDIR\DefApp32.txt
}
write-host "File " $TARGETDIR"\DefApp32.txt Created"
}
:CompareApps.ps1 creates current list of applications and compare with Baseline
cls
$CurDir = $myinvocation.InvocationName | split-path -parent
Function Abort
{
Write-Host "Script Aborted"
}
$DOCDIR = [Environment]::GetFolderPath("MyDocuments")
$TARGETDIR = "$DOCDIR\AppLog"
$COMPNAME = $env:computername
if(!(Test-Path -Path $TARGETDIR ))
{
Write-Host $TARGETDIR" Folder does not Exist"
Abort
}
Function Finish
{
write-host "Comparing Products Completed"
}
Function CreateCompNameFile
{
Get-WmiObject -Class Win32_Product | Select-Object -Property Name, Version > $TARGETDIR\$COMPNAME.txt
write-host "File"$TARGETDIR\$COMPNAME".txt Created"
}
Function Compare64
{
write-host "Comparing Products with file"$TARGETDIR\$COMPNAME".txt"
Compare-Object -ReferenceObject (Get-Content $TARGETDIR\DefApp64.txt) -DifferenceObject (Get-Content $TARGETDIR\$COMPNAME.txt)
Finish
}
Function Compare32
{
write-host "Comparing Products with file"$TARGETDIR\$COMPNAME".txt"
Compare-Object -ReferenceObject (Get-Content $TARGETDIR\DefApp32.txt) -DifferenceObject (Get-Content $TARGETDIR\$COMPNAME.txt)
Finish
}
cls
if ([environment]::Is64BitOperatingSystem)
{
Write-Host "64bit Windows Detected"
Write-Host "Collecting Product Information for"$COMPNAME
If (Test-Path $TARGETDIR\$COMPNAME.txt)
{
Remove-Item $TARGETDIR\$COMPNAME.txt
CreateCompNameFile
}
else
{
CreateCompNameFile
}
Compare64
}
else
{
Write-Host "32bit Windows Detected"
Write-Host "Collecting Product Information for"$COMPNAME
If (Test-Path $TARGETDIR\$COMPNAME.txt)
{
Remove-Item $TARGETDIR\$COMPNAME.txt
CreateCompNameFile
}
else
{
CreateCompNameFile
}
Compare32
}
I would prefer to run this code via excel and capture the list in a worksheet, then compare the baseline which is already in the worksheet. How do I execute or use commands from PowerShell in VBA?
Previously I have used (see post How to list all installed applications in to excel)
Set objWMIService = GetObject("winmgmts:{impersonationLevel=impersonate}!\\" & StrComputer & "\root\cimv2")
Set objAllSoftwares = objWMIService.ExecQuery("Select * from Win32_Product")
For some reason it does not retrieve all the software installed (which you can see in Add/Remove Programs) e.g. 7-Zip (64 bit), GPL Ghost Script to name a few
Any help would be much appreciated.
Thanks
Dumidu
In the next week we want to relocate our database from one server to another one.
On http://msdn.microsoft.com/en-us/library/ms187858%28v=sql.110%29.aspx
I read about detaching the database from the old location and attach it to the new location.
The problem is, that I don't have access to the file system of the server, I don't even know where exactly the server is physically located^^
Is there a way to relocate a database from one Server to another without the need to access the file system of the old Server?
You could use the Import/Export tool in SQL Server to copy the data directly which will create a new database in the destination location. The good thing about this is the new DB will work as you might expect since it is created from scratch on the target server, but that also means that you might have old, deprecated syntax in your stored procs or functions or whatever which won't work unless you lower the compatibility level (although that shouldn't be hard). also be aware of any possible collation conflicts (your old server might have SQL_Latin1_General_CP1_CI_AS and the new one might be Latin1_General_CI_AS which can cause equality operations to fail amongst other things).
In addition, if you have a big database then it'll take a long time, but I can't think of any other method off the of of my head which doesn't require some level of access to the file system as you'd still need to get to the file system to take a copy of a backup, or if using a UNC path for the backup the source server would need to be able to write to that location and you'd need to be able to access it afterwards. If anyone else can think of one I'd be interested because it would be a useful bit of knowledge to have tucked away.
Edit:
Should also have mentioned the use of Powershell and SMO - it's not really any different to using the Import/Export wizard but it does allow you to fine tune things. The following is a PS script I have been using to create a copy of a DB (schema only) on a different server to the original but with certain facets missing (NCIs, FKs Indeitites etc) as the copy was destined to be read-only. You could easily extend it to copy the data as well.
param (
[string]$sourceServerName = $(throw "Source server name is required."),
[string]$destServerName = $(throw "Destination server is required."),
[string]$sourceDBName = $(throw "Source database name is required."),
[string]$destDBName = $(throw "Destination database name is required"),
[string]$schema = "dbo"
)
# Add an error trap so that at the end of the script we can see if we recorded any non-fatal errors and if so then throw
# an error and return 1 so that the SQL job recognises there's been an error.
trap
{
write-output $_
exit 1
}
# Append year to destination DB name if it isn't already on the end.
$year = (Get-Date).AddYears(-6).Year
if (-Not $destDBName.EndsWith($year)) {
$destDBName+=$year
}
# Load assemblies.
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.ConnectionInfo") | out-null
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.SMO") | out-null
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.SmoExtended") | out-null
# Set up source connection.
$sourceSrvConn = new-object Microsoft.SqlServer.Management.Common.ServerConnection
$sourceSrvConn.ServerInstance = $sourceServerName
$sourceSrvConn.LoginSecure = $false
$sourceSrvConn.Login = "MyLogin"
$sourceSrvConn.Password = "xxx"
# Set up destination connection.
$destSrvConn = new-object Microsoft.SqlServer.Management.Common.ServerConnection
$destSrvConn.ServerInstance = $destServerName
$destSrvConn.LoginSecure = $false
$destSrvConn.Login = "MyLogin"
$destSrvConn.Password = "xxx"
$sourceSrv = New-Object Microsoft.SqlServer.Management.SMO.Server($sourceSrvConn)
$sourceDb = New-Object ("Microsoft.SqlServer.Management.SMO.Database")
$destSrv = New-Object Microsoft.SqlServer.Management.SMO.Server($destSrvConn)
$destDb = New-Object ("Microsoft.SqlServer.Management.SMO.Database")
$tbl = New-Object ("Microsoft.SqlServer.Management.SMO.Table")
$scripter = New-Object Microsoft.SqlServer.Management.SMO.Scripter($sourceSrvConn)
# Get the database objects
$sourceDb = $sourceSrv.Databases[$sourceDbName]
$destDb = $destSrv.Databases[$destDbName]
# Test to see databases exist. Not as easy to test for servers - if you got those wrong then this will fail and throw an error
# so it's down to the user to check their values carefully.
if ($sourceDb -eq $null) {throw "Database '" + $sourceDbName + "' does not exist on server '" + $sourceServerName + "'"}
if ($destDb -eq $null) {throw "Database '" + $destDbName + "' does not exist on server '" + $destServerName + "'"}
# Get source objects.
$tbl = $sourceDb.tables | Where-object { $_.schema -eq $schema -and -not $_.IsSystemObject }
$storedProcs = $sourceDb.StoredProcedures | Where-object { $_.schema -eq $schema -and -not $_.IsSystemObject }
$views = $sourceDb.Views | Where-object { $_.schema -eq $schema -and -not $_.IsSystemObject }
$udfs = $sourceDb.UserDefinedFunctions | Where-object { $_.schema -eq $schema -and -not $_.IsSystemObject }
$catalogs = $sourceDb.FullTextCatalogs
$udtts = $sourceDb.UserDefinedTableTypes | Where-object { $_.schema -eq $schema -and -not $_.IsSystemObject }
$assemblies = $sourceDb.Assemblies | Where-object { -not $_.IsSystemObject }
# Set scripter options to ensure only schema is scripted
$scripter.Options.ScriptSchema = $true;
$scripter.Options.ScriptData = $false;
#Exclude GOs after every line
$scripter.Options.NoCommandTerminator = $false;
$scripter.Options.ToFileOnly = $false
$scripter.Options.AllowSystemObjects = $false
$scripter.Options.Permissions = $true
$scripter.Options.DriForeignKeys = $false
$scripter.Options.SchemaQualify = $true
$scripter.Options.AnsiFile = $true
$scripter.Options.Indexes = $false
$scripter.Options.DriIndexes = $false
$scripter.Options.DriClustered = $true
$scripter.Options.DriNonClustered = $false
$scripter.Options.NonClusteredIndexes = $false
$scripter.Options.ClusteredIndexes = $true
$scripter.Options.FullTextIndexes = $true
$scripter.Options.NoIdentities = $true
$scripter.Options.DriPrimaryKey = $true
$scripter.Options.EnforceScriptingOptions = $true
$pattern = "(\b" + $sourceDBName + "\b)"
$errors = 0
function CopyObjectsToDestination($objects) {
foreach ($o in $objects) {
if ($o -ne $null) {
try {
$script = $scripter.Script($o)
$script = $script -replace $pattern, $destDBName
$destDb.ExecuteNonQuery($script)
} catch {
#Make sure any errors are logged by the SQL job.
$ex = $_.Exception
$message = $o.Name + " " + (Get-Date)
$message += "`r`n"
#$message += $ex.message
$ex = $ex.InnerException
while ($ex.InnerException) {
$message += "`n$ex.InnerException.message"
$ex = $ex.InnerException
}
#Write-Error $o.Name
Write-Error $message # Write to caller. SQL Agent will display this (or at least some of it) in the job step history.
# Need to use Set-Variable or changes to the variable will only be in scope within the function and we want to persist this.
if ($errors -eq 0) {
Set-Variable -Name errors -Scope 1 -Value 1
}
}
}
}
}
# Output the scripts
CopyObjectsToDestination $assemblies
CopyObjectsToDestination $tbl
CopyObjectsToDestination $udfs
CopyObjectsToDestination $views
CopyObjectsToDestination $storedProcs
CopyObjectsToDestination $catalogs
CopyObjectsToDestination $udtts
# Disconnect from databases cleanly.
$sourceSrv.ConnectionContext.Disconnect()
$destSrv.ConnectionContext.Disconnect()
# Did we encounter any non-fatal errors along the way (SQL errors and suchlike)? If yes then throw an exception which tells the
# user to check the log files.
if ($errors -eq 1) {
throw "Errors encountered - see log file for details"
}
In Powershell it is posible to increase a Variable when a Error ocours.
Per Exemple when I run this command twice the command fails (because the directory already exists) and the Error Variable now icrease to 1.
New-Item C:\TEMP\blabla -type directory -EA continue -EV +err
write-host $err.count
#Output: 1
But now to my Question:
I'd like to increase the $err variable manually but that does not work.
I tried the following:
switch ($LASTEXITCODE) {
0 {
Write-Host "Success" -ForegroundColor Green
break
}
2 {
$err.count++
break
}
default {
$err.count++
break
}
Error:
At C:\windows\mscripts\100_create_website_servicelayer.ps1:33 char:9
+ $err. <<<< count++
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : PropertyAssignmentException
$err has a count property just because is of type array.
You can try to add some string to $err in each case:
2 {
$err += "Error"
break
}
default {
$err += "Error"
break
}