I have a powershell script that we use during a Microsoft SCCM PXE task sequence for naming a PC. It worked flawlessly until a recent upgrade to SCCM 2012 R2 by the primary server admin.
Now when the code runs search if a user is in a specified AD group needed to complete the PXE build it gives this COM error
Exception calling "FindAll" with "0" argument(s): "Unknown error (0x80005000)"
At X:\Windows\System32\OSD\x86_PXE.ps1:202 char:1
+ $colResults = $objSearcher.FindAll() # Finds all items that match search and put ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : COMException
I have searched far and wide to try and solve this. It seems like a .Net error but I have been unsuccessful in resolving it.
Below is the relevant code. Note that this is being ran in Windows PE that is included with SCCM 2012 R2 as well as the current Windows ADK. It is most likely going to work just fine on a normal PC as it does on mine.
Things to note, you will need to change to match you environments
$Domain
$strFilter - specifically "Memberof=cn="
$objOU - server path
function get-humadcreds {
$global:creds = get-credential -message "Please authenticate to Domain"
$global:UserName = $creds.username
$global:encPassword = $creds.password
$password = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($encpassword)) # Converts secure string to plain text
$Domain = #Domain
Add-Type -AssemblyName System.DirectoryServices.AccountManagement
$ct = [System.DirectoryServices.AccountManagement.ContextType]::Domain
$pc = New-Object System.DirectoryServices.AccountManagement.PrincipalContext $ct,$Domain
$authed = $pc.ValidateCredentials($UserName,$Password)
# Recursively requests credentials if authorization fails
if ($authed -eq $false) {
[System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
[System.Windows.Forms.MessageBox]::Show("Authentication failed - please retry!")
get-humadcreds
}
}
get-humadcreds # Gets AD credentials from user
###Provisioning Authentication
$strFilter = "(&(objectCategory=user)(SAMACCOUNTNAME=$global:UserName)(|(Memberof=cn=,OU=Delegation,OU=,dc=,dc=,dc=)))" # Filter for searching
$decodedpassword = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($encpassword)) # Decoded password from AD Auth
$objOU = New-Object System.DirectoryServices.DirectoryEntry("LDAP://server/OU=,dc=,dc=,dc=",$global:username,$decodedpassword) # Authentication must specify domain controller
$objDomain = New-Object System.DirectoryServices.DirectoryEntry
$objSearcher = New-Object System.DirectoryServices.DirectorySearcher
$objSearcher.SearchRoot = $objOU # Starts search in this OU
$objSearcher.PageSize = 1000
$objSearcher.Filter = $strFilter # Applies filter to search
$objSearcher.SearchScope = "Subtree"
$colProplist = "name"
$isInProvGroup = $False # Defaults value to false.
echo $objSearcher >> X:\Windows\System32\OSD\results.txt
$colResults = $objSearcher.FindAll() # Finds all items that match search and puts them in array $colResults
echo $colResults
foreach ($objResult in $colResults){
$isInProvGroup=$True #If user is in a group to add PCs (if $colResults is not empty), result will be true
}
echo $isInProvGroup
PE OS Verson 6.3.9600.16384
Welp.. found my answer, fixed it Aug 11th. Reddit thread.
Previously in SCCM 2012 prior to R2 the boot image was a Windows 8 PE4 image in which we had to integrated ADSI back into to using a version of it written by Johan Arwidmark. This can be found here for reference.
This time around after the R2 update and subsequently the forced upgrade of the boot images to 8.1 PE5 since no prior boot images would boot from PXE we had to add ADSI back in again this time from here. Previously and this time it was done through the configuration manager under drivers, its added as a driver with its required files and is added as a driver component into the boot.wim but in reality after digging for quite some time I found that it wasn't actually adding the needed dll files into the image even though the operation returned successful.
What I ended up doing was manually mounting the wim file on my PC with DISM, adding the driver from a folder, allowing unsigned ones to be installed. then manually verified the dlls were put into
place in the mounted System32 folder. After I did that I was able to unmount the wim committing changes, replace the boot wim used by the server, distribute content and test it. Which was successful.
Just as a reference, the required files are listed below and are also in the readme's. In my case they had to come from a Windows 8.1 32bit install. If going for 64bit they have to come from a computer or image with Windows 8.1 64bit
adsldp.dll
adsmsext.dll
adsnt.dll
mscoree.dll
mscorier.dll
mscories.dll
Related
I'm writing a powershell 5.1 script to query info from a mdb file based on this article. I get this error message when I try to open/access an mdb file:
MDB Open next
Provider cannot be found. It may not be properly installed.
I'm pretty sure I need to adjust my connection info according to what I have installed. This is what I'm doing:
$pathViewBase = 'C:\Data\EndToEnd_view\' #View dir.
$XML_MDB_Dirs = #('\AppText.mdb') #more files later
foreach($mdbFile in $XML_MDB_Dirs)
{
$pathToMdb = Join-Path -Path $pathViewBase -ChildPath $mdbFile
if(Test-Path $pathToMdb)
{
$cn = new-object -comobject ADODB.Connection
$rs = new-object -comobject ADODB.Recordset
Write-Host "MDB Open next" -ForegroundColor DarkCyan
$cn.Open("Provider = Microsoft.Jet.OLEDB.4.0;Data Source = $pathToMdb") ###error this line
Write-Host "Open done" -ForegroundColor DarkCyan
$rs.Open(“SELECT TOP 1 [TableName].[Message Description],
[TableName].[Column1]
FROM [TableName]
WHERE [TableName].[Message Description] = 'ERROR'”,
$cn, $adOpenStatic, $adLockOptimistic)
$rs.MoveFirst()
Write-Host "Message value obtained for ERROR: " $rs.Fields.Item("Name").Value
Break ##########################
}#test-Path
}#foreach
I found this regarding odbc connections, and it seems to say I need to adjust my connection info. Looking at what's installed on my computer, I see this, but I'm a little unclear what I need to adjust my open code to use. Would I need to replace 'Microsoft.Jet.OLEDB.4.0' with 'SQLNCLI11.dll'?
Update:
I checked,
(Get-WmiObject Win32_OperatingSystem).OSArchitecture
and I am running 64 bit powershell, so since I installed accessdatabaseengine_x64.exe access database engine, per #
Mathias R. Jessen below (and rebooted), that's correct. But I still get the same error. I'm not sure if there's something I could check to see if it's installed correctly, or if I need to use SQLNCLI11.0 as the provider instead of Microsoft.Jet.OLEDB.4.0, or if I need to add "using" at the top? Or do I need to check if a powershell module is installed?
I am trying to construct a Powershell script that leverages the WinSCP binaries to download files from an FTP server.
The script so far is as follows (minus actual IPs and folder paths):
# Load WinSCP .NET assembly
Add-Type -Path "WinSCPnet.dll"
# Setup session options
$sessionOptions = New-Object WinSCP.SessionOptions -Property #{
Protocol = [WinSCP.Protocol]::Ftp
FtpMode = [WinSCP.FtpMode]::Passive
HostName = "ftp server ip address"
UserName = "ftp-username"
Password = "ftp-password"
}
$session = New-Object WinSCP.Session
try
{
# Connect
$session.Open($sessionOptions)
# Download files
$session.GetFiles("/home/ftp-username/uploads/*.txt", "C:\temp\").Check()
}
finally
{
# Disconnect, clean up
$session.Dispose()
}
I keep on getting the following error:
Exception calling "Open" with "1" argument(s): "Connection failed.
Timeout detected. (control connection)
Connection failed."
At C:\winscp-ftp.ps1:18 char:5
+ $session.Open($sessionOptions)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : SessionRemoteException
I did some reading and suggestions say that FTPMode Passive will solve this, but even after including that, the error persists.
Any advice/guidance will be appreciated.
Windows 10 64-bit. Winscp v.5.1
I was getting the same error message. I used WinSCP to generate the script and it started working.
My guess, and it is a weak one at that, is your hostname and/or username and/or password is malformed. If you use WinSCP to generate the script it has a "copy to clipboard" function.
This works on my ftp server. I don't know why. My code is hardly different than yours. Could it be whitespace after hostname, username, password?
Use the full path to winscpnet.dll
I don't think it makes any difference whether you use $sessionOptions.AddRawSettings("ProxyPort", "0")
It worked whether I did passive or not, which is odd because I always have to use passive.
I connected to my ftp server and downloaded files to c:\temp
I installed Posh-SSH 2.2 but the error message continued. I un-installed Posh-SSH 2.2 before getting the script to work.
If you use WinSCP to generate the code it does not tell you to use the full path to winscpnet.dll
It works after logging off and on.
It works after a restart.
It works in a window w/ or w/o admin privileges.
# Load WinSCP .NET assembly. If you are not in the winscp directory use the full path.
Add-Type -Path "C:\Program Files (x86)\WinSCP\WinSCPnet.dll"
# Set up session options
$sessionOptions = New-Object WinSCP.SessionOptions -Property #{
Protocol = [WinSCP.Protocol]::Ftp
FtpMode = [WinSCP.FtpMode]::Passive
HostName = "ftp server ip address"
UserName = "ftp-username"
Password = "ftp-password"
}
$sessionOptions.AddRawSettings("ProxyPort", "0")
$session = New-Object WinSCP.Session
try
{
# Connect
$session.Open($sessionOptions)
# Download files
$session.GetFiles("/home/ftp-username/uploads/*.txt", "C:\temp\").Check())
}
finally
{
# Disconnect, clean up
$session.Dispose()
}
I have to implement a solution where I have to deploy a SSIS project (xy.ispac) from one machine to another. So far I've managed to copy-cut-paste the following stuff from all around the internet:
# Variables
$ServerName = "target"
$SSISCatalog = "SSISDB" # sort of constant
$CatalogPwd = "catalog_password"
$ProjectFilePath = "D:\Projects_to_depoly\Project_1.ispac"
$ProjectName = "Project_name"
$FolderName = "Data_collector"
# Load the IntegrationServices Assembly
[Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.Management.IntegrationServices")
# Store the IntegrationServices Assembly namespace to avoid typing it every time
$ISNamespace = "Microsoft.SqlServer.Management.IntegrationServices"
Write-Host "Connecting to server ..."
# Create a connection to the server
$sqlConnectionString = "Data Source=$ServerName;Initial Catalog=master;Integrated Security=SSPI;"
$sqlConnection = New-Object System.Data.SqlClient.SqlConnection $sqlConnectionString
$integrationServices = New-Object "$ISNamespace.IntegrationServices" $sqlConnection
$catalog = $integrationServices.Catalogs[$SSISCatalog]
# Create the Integration Services object if it does not exist
if (!$catalog) {
# Provision a new SSIS Catalog
Write-Host "Creating SSIS Catalog ..."
$catalog = New-Object "$ISNamespace.Catalog" ($integrationServices, $SSISCatalog, $CatalogPwd)
$catalog.Create()
}
$folder = $catalog.Folders[$FolderName]
if (!$folder)
{
#Create a folder in SSISDB
Write-Host "Creating Folder ..."
$folder = New-Object "$ISNamespace.CatalogFolder" ($catalog, $FolderName, $FolderName)
$folder.Create()
}
# Read the project file, and deploy it to the folder
Write-Host "Deploying Project ..."
[byte[]] $projectFile = [System.IO.File]::ReadAllBytes($ProjectFilePath)
$folder.DeployProject($ProjectName, $projectFile)
This seemed to be working surprisingly well on the development machine - test server pair. However, the live environment will be a bit different, the machine doing the deployment job (deployment server, or DS from now on) and the SQL Server (DB for short) the project is to be deployed are in different domains and since SSIS requires windows authentication, I'm going to need to run the above code locally on DS but using credentials of a user on the DB.
And that's the point where I fail. The only thing that worked is to start the Powershell command line interface using runas /netonly /user:thatdomain\anuserthere powershell, enter the password, and paste the script unaltered into it. Alas, this is not an option, since there's no way to pass the password to runas (at least once with /savecred) and user interactivity is not possible anyway (the whole thing has to be automated).
I've tried the following:
Simply unning the script on DS, the line $sqlConnection = New-Object System.Data.SqlClient.SqlConnection $sqlConnectionString would use the credentials from DS which is not recognized by DB, and New-Object does not have a -Credential arg that I could pass to
Putting everything into an Invoke-Command with -Credential requires using -Computername as well. I guess it would be possible to use the local as 'remote' (using . as Computername) but it still complains about access being denied. I'm scanning through about_Remote_Troubleshooting, so far without any success.
Any hints on how to overcome this issue?
A solution might be to use a sql user (with the right access rights) instead of an AD used.
Something like this should work.
(Check also the answer to correct the connection string)
I'm hoping somebody can shed light on this, because it has been driving me to distraction.
I have a script which will save the reports it creates to a sharepoint document library via UNC path, if the path exists, otherwise it saves to the UNC path of a network drive location as a fallback.
I've noticed that checking with test-path, saving (through an msexcel COM object) or trying to open the folder in windows explorer using invoke-item only work if I had already accessed the sharepoint site (via web browser or windows explorer) since the PC last logged on (I'm running Windows 7 Enterprise Service Pack 1 - 64-bit edition).
If I haven't yet been on to sharepoint manually since last logon, test-path returns false, and the other methods cause ItemNotFoundException e.g.
ii : Cannot find path '\\uk.sharepoint.mydomain.local\sites\mycompany\myteam\Shared Documents\Reports' because it does not exist.
At line:1 char:1
+ ii '\\uk.sharepoint.mydomain.local\sites\mycompany\myteam\Shared Document ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (\\uk.sharepoint...\Reports:String) [Invoke-Item], ItemNotFoundException
+ FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.InvokeItemCommand
Example areas of code:
$LANPath = "\\myserver\myshare\teamdirs\scriptdir"
$SharepointPath = "\\uk.sharepoint.mydomain.local\sites\mycompany\myteam\Shared Documents\Reoprts"
$ScriptPath = $LANPath + "\bin"
If (Test-Path $SharepointPath) {$BasePath = $SharepointPath;write-host "Using sharepoint to save reports"} else {$BasePath = "$LANPath\Reports";write-host "Using LAN to save reports - sharepoint not accessible"}
and
$_|select -expandproperty HTMLBody | Out-File $($BasePath + "\Eml_body.html")
Write-Host "Reformating HTML"
$html = New-Object -ComObject "HTMLFile";
$source = Get-Content -Path ($BasePath + "\Eml_body.html") -Raw;
and when saving the excel spreadsheet from within my COM object:
$workbook._SaveAs($fileout,[Microsoft.Office.Interop.Excel.XlFileFormat]::xlOpenXMLWorkbook,$Missing,$Missing,$false,$false,[Microsoft.Office.Interop.Excel.XlSaveAsAccessMode]::xlNoChange,[Microsoft.Office.Interop.Excel.XlSaveConflictResolution]::xlLocalSessionChanges,$true,$Missing,$Missing)
You should be able to use a System.Net.WebClient object to access SharePoint file locations.
$client = New-Object System.Net.WebClient
The documentation for the WebClient.Credentials property suggests that the default credentials in this case may be for the ASP.NET server-side process rather than the current user's credentials:
If the WebClient class is being used in a middle tier application, such as an ASP.NET application, the DefaultCredentials belong to the account running the ASP page (the server-side credentials). Typically, you would set this property to the credentials of the client on whose behalf the request is made.
You therefore may want to set the credentials manually. You can plug them in as plain text...
$client.Credentials = New-Object System.Net.NetworkCredential("username","pswd","domain")
...or you could prompt the current user for their credentials.
$client.Credentials = Get-Credential
Here's an example that grabs a file and writes its content to the screen:
$client = New-Object System.Net.WebClient
$client.Credentials = Get-Credential
$data = $client.OpenRead("http://yoursharepointurl.com/library/document.txt")
$reader = New-Object System.IO.StreamReader($data)
$results = $reader.ReadToEnd()
Write-Host $results
$data.Close()
$reader.Close()
I know this is an old thread but for those searching, check out this link: https://www.myotherpcisacloud.com/post/Sometimes-I-Can-Access-the-WebDAV-Share-Sometimes-I-Cant!
Because SharePoint exposes its shares over WebDav, you need to ensure the WebClient service is running on the machine from which you are accessing the path. Browsing the path in explorer automatically fires up the service, while command-line methods do not.
If you change the startup type of WebClient to Automatic, it should resolve the issue.
why does $store.Open($openFlags) throw an exception, and is there a better way than my "work around" to make it work?
<#
$store = New-Object System.Security.Cryptography.X509Certificates.X509Store("Cert:\CurrentUser\My")
$openFlags = [System.Security.Cryptography.X509Certificates.OpenFlags]::MaxAllowed
$store.Open($openFlags) #Exception calling "Open" with "1" argument(s): "The parameter is incorrect.
#>
#Work Around:
$store = New-Object System.Security.Cryptography.X509Certificates.X509Store("Cert:\CurrentUser\My")
$openFlags = [System.Security.Cryptography.X509Certificates.OpenFlags]::MaxAllowed
$startIndexOfStoreName = $store.Name.LastIndexOf("\") + 1
$lengthOfStoreName = $store.Name.Length - $startIndexOfStoreName
$storeNameString = $store.Name.Substring($startIndexOfStoreName, $lengthOfStoreName)
$storeName = [System.Security.Cryptography.X509Certificates.StoreName]$storeNameString
$store = New-Object System.Security.Cryptography.X509Certificates.X509Store($storeName, $store.Location)
$store.Open($openFlags) #No Exception thrown!
Update: Seems as though when using the X509Store(String) constructor, you are NOT allowed to have any slashes (correct me if I'm wrong). So $store = New-Object System.Security.Cryptography.X509Certificates.X509Store("My") works.
Define you certificate store using
$store = Get-Item "Cert:\CurrentUser\My"
instead of
$store = New-Object System.Security.Cryptography.X509Certificates.X509Store("Cert:\CurrentUser\My")
To be honest I'm still trying to figure out why it works, or how.
The first method returns a $store called "My", so I'm assuming that it targets the store specifically and you can open it with
$store.Open($openFlags)
The second method returns a $store called "Cert:\CurrentUser\My". Open method on this will fail.
I wanted to comment on this, since, as is already pointed out, "the mixing of .NET Framework and the use of PowerShell Providers" in the previous examples. For me, I needed this to work as a pure .NET way of getting the certs to test out some C# equivalent code without the full development environment on a users computer.
Here's what I came up with, which worked:
$Location = [Security.Cryptography.X509Certificates.StoreLocation]::CurrentUser
$StoreName = [Security.Cryptography.X509Certificates.StoreName]::My
$Store = New-Object System.Security.Cryptography.X509Certificates.X509Store -ArgumentList $StoreName, $Location
$OpenFlags = [System.Security.Cryptography.X509Certificates.OpenFlags]::ReadOnly
$Store.Open($OpenFlags)
$Store.Certificates
Actually, you are mixing methods. One is via a provider (Cert:) the other is a .Net type (X509Store). Very different processes for attaching to the cert stores and pulling cert details.
Think of "Cert:" like a PSDrive (which it basically is). So you can get-childitem, etc. and don't need to "open" the store. In this mindset, the cert store locations are folders, and certs are individual objects:
# List the store locations
gci Cert:\
# List store names in CurrentUser store location
gci Cert:\CurrentUser
# List certs in the My store of CurrentUser store location
gci Cert:\CurrentUser\My | format-list
The catch to using the Cert: provider is that if you want to work with certs on remote systems, remoting (WinRM) needs to be enabled so you can "Invoke-Command". Not every environment allows this. That is where the .Net X509Store comes in. Not sure how well it works with "CurrentUser", but I've never been concerned about that - I am more interested in what is in the "LocalMachine" stores (specifically "My" since that is where the system holds web and auth certs). Modified snippet to list these certs (pulled from a script I built for interrogating all the servers in SharePoint farms).
# Change as necessary
$strTarget = $env:computername
$strCertStoreLocation = 'LocalMachine'
$strCertStoreName = 'My'
# Set up store parameters, connect and open store
[System.Security.Cryptography.X509Certificates.StoreLocation]$strStoreLoc = [String]$strCertStoreLocation
[System.Security.Cryptography.X509Certificates.StoreName]$strStoreName = [String]$strCertStoreName
$objCertStore = New-Object System.Security.Cryptography.X509Certificates.X509Store -ArgumentList "\\$($strTarget)\$($strStoreName)", $strStoreLoc
$objCertStore.Open('ReadOnly')
# List cert details in bulk
$objCertStore.Certificates | Format-List
# List specific props
foreach ($Cert in $objCertStore.Certificates) {
"Subject: $($Cert.Subject)"
"Issuer: $($Cert.Issuer)"
"Issued: $($Cert.NotBefore)"
"Expires: $($Cert.NotAfter)"
""
}
For a bit more details about each, hit up your favorite tech repository (MSDN, PowerShell.org, Hey Scripting Guy, etc.) :)