Powershell Pull Current OU - vb.net

I need to be able to pull the current machine OU. I found some VB code that could do this,but I would like to just be able to do in the script with out having to call VB. Any ideas, the VB code is below.
Set objSysInfo = CreateObject("ADSystemInfo")
DN = objSysInfo.ComputerName
WScript.Echo DN
-Josh

You can get the ADSystemInfo with this function.
function Get-LocalLogonInformation
{
try
{
$ADSystemInfo = New-Object -ComObject ADSystemInfo
$type = $ADSystemInfo.GetType()
New-Object -TypeName PSObject -Property #{
UserDistinguishedName = $type.InvokeMember('UserName','GetProperty',$null,$ADSystemInfo,$null)
ComputerDistinguishedName = $type.InvokeMember('ComputerName','GetProperty',$null,$ADSystemInfo,$null)
SiteName = $type.InvokeMember('SiteName','GetProperty',$null,$ADSystemInfo,$null)
DomainShortName = $type.InvokeMember('DomainShortName','GetProperty',$null,$ADSystemInfo,$null)
DomainDNSName = $type.InvokeMember('DomainDNSName','GetProperty',$null,$ADSystemInfo,$null)
ForestDNSName = $type.InvokeMember('ForestDNSName','GetProperty',$null,$ADSystemInfo,$null)
PDCRoleOwnerDistinguishedName = $type.InvokeMember('PDCRoleOwner','GetProperty',$null,$ADSystemInfo,$null)
SchemaRoleOwnerDistinguishedName = $type.InvokeMember('SchemaRoleOwner','GetProperty',$null,$ADSystemInfo,$null)
IsNativeModeDomain = $type.InvokeMember('IsNativeMode','GetProperty',$null,$ADSystemInfo,$null)
}
}
catch
{
throw
}
}

You can't use ADSystemInfo directly in Powershell (or at least it's not easy) according to this page
Well, OK, that’s not entirely true; it is possible to use ADSystemInfo from within PowerShell; however, the process is far from easy and even farther from being intuitive. That’s because ADSystemInfo is lacking a “wrapper” that makes it easy to access the object from a .NET language like Windows PowerShell. That results in a lot of gyrations involving .NET Reflection classes, the InvokeMember method, and, as near as we can tell, a lot of prayer.
But the page does provide examples for performing AD queries using the System.DirectoryServices.DirectorySearcher .NET object. Here's an example from the page slightly modified to match your VB script:
$strName = $env:computername
$strFilter = "(&(objectCategory=Computer)(Name=$strName))"
$objSearcher = New-Object System.DirectoryServices.DirectorySearcher
$objSearcher.Filter = $strFilter
$objPath = $objSearcher.FindOne()
$objPath.GetDirectoryEntry().distinguishedname

Related

How to get datasource from Connectionstring using PowerShell?

In .Net we can get the datasource from a connectionstring using below mechanism:
System.Data.SqlClient.SqlConnectionStringBuilder builder = new System.Data.SqlClient.SqlConnectionStringBuilder(connectionString);
string server = builder.DataSource;
I was trying to do that in PowerShell but getting the following exception:
$ConstringObj = New-Object System.Data.SqlClient.SqlConnectionStringBuilder($conString)
New-Object : Exception calling ".ctor" with "1" argument(s): "Keyword
not supported: 'metadata'." At line:1 char:17
+ $ConstringObj = New-Object System.Data.SqlClient.SqlConnectionStringBuilder($con ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [New-Object], MethodInvocationException
+ FullyQualifiedErrorId : ConstructorInvokedThrowException,Microsoft.PowerShell.Commands.NewObjectCommand
How to do that in PowerShell?
Problem
There's some weird behavior when using SqlConnectionStringBuilder in PowerShell - let me explain
Since it's a dotnet class, you'd expect all of the same properties and methods available in C#
For example, this works fine in C#:
var cnnBuilder = new SqlConnectionStringBuilder();
cnnBuilder.DataSource = "server_name";
cnnBuilder.InitialCatalog = "db_name";
So the equivalent code in PS, should work:
$cnnBuilder = New-Object System.Data.SqlClient.SqlConnectionStringBuilder
$cnnBuilder.DataSource = "server_name"
$cnnBuilder.InitialCatalog = "db_name"
However, SqlConnectionStringBuilder is built ontop of DbConnectionStringBuilder which implements IDictionary so fundamentally we're working with a dictionary object that has some syntactic sugar wrappers
.NET resolves this with an override on the dictionary accessors and setters like this (simplified here):
public override object this[string keyword] {
get {
Keywords index = GetIndex(keyword);
return GetAt(index);
}
set {
Keywords index = GetIndex(keyword);
switch(index) {
case Keywords.DataSource: DataSource = ConvertToString(value); break;
case Keywords.InitialCatalog: InitialCatalog = ConvertToString(value); break;
// ***
}
}
}
So really, it's taking the DataSource property and mapping it to the "Data Source" key (with space)
Whenever PS assigns or retrieves a value, it has to decide whether to use the underlying dictionary implementation or the property. And when you look for DataSource in the dictionary (without the space), that sql connection keyword doesn't exist.
Solutions
Opt 1 - Use Dictionary Names
You can use the bracket or dot notation with the actual sql key to access the entry in the hashtable
$cnnBuilder = New-Object System.Data.SqlClient.SqlConnectionStringBuilder
$cnnBuilder["Data Source"] = "server_name"
$cnnBuilder."Initial Catalog" = "db_name"
Opt 2 - Use PSBase
PSBase returns the "raw view of the object" and will give us the default behavior in dotnet
$cnnBuilder = New-Object System.Data.SqlClient.SqlConnectionStringBuilder
$cnnBuilder.PSBase.DataSource = "server_name"
$cnnBuilder.PSBase.InitialCatalog = "db_name"
Opt 3 - Use -Property Parameter
During the construction, you can set the -Property parameter on New-Object which "sets property values and invokes methods of the new object."
$cnnBuilder = New-Object System.Data.SqlClient.SqlConnectionStringBuilder `
-Property #{
DataSource = "server_name"
InitialCatalog = "db_name"
}
Additional Reading
Using SQLConnection object in PowerShell
Your example should work. However, you could also grab the datasource using a regex:
[regex]::Match($ConstringObj, 'Data Source=([^;]+)').Groups[1].Value

PowerPoint to PDF: Minimum size option

I was attempting to convert PowerPoint files to PDF using PowerShell and was able to do so. However, I am trying to take this one step further and select the 'Minimum Size (publishing online)' option through the script.
Is there a property that needs to be set for this to happen? I'm guessing it is the $ppQualityStandard variable but not exactly sure.
EDIT: This is what I am using currently:
function ppt_to_pdf ($folderpath, $pptname) {
Add-Type -AssemblyName office
$ppFormatPDF = 2
$ppQualityStandard = 0
$p = New-Object -ComObject PowerPoint.Application
$p.Visible = [Microsoft.Office.Core.MsoTriState]::msoTrue
$ppt = $p.Presentations.Open("$folderpath\$pptname")
$ppt.SaveCopyAs("$folderpath\$pptname", 32)
$ppt.Close()
$p.Quit()
$p = $null
[gc]::collect()
[gc]::WaitForPendingFinalizers()
}
I suspect you need to use .ExportAsFixedFormat rather than .SaveCopyAs.
It takes, among other parameters, Intent as type ppFixedFormatIntent, which can be either:
ppFixedFormatIntentScreen (=1)
or
ppFixedFormatIntentPrint (=2)
There's a host of other parms. To learn more, start PPT, go into the VBA IDE and press F2 for the Object Browser and search for ExportAsFixedFormat

Why is my SQL executed via Start-Job not returning the answer I expect?

I have a database hosted on SQL Server 2008 which my regular user account does not have access to. In order to query it, I need to use my special "admin" account (just another AD account, but in different groups from my regular user account).
I came up with an idea to use background jobs in Powershell via Start-Job to run queries against this database, as you can start the job with different credentials from your logged in user, and thus integrated security on the database works properly. Since my issue, I've googled a lot this afternoon and seen a few people adopt this approach for the same reason, but their results seem to actually work - whereas mine isn't for some reason.
I have the follow powershell code:
[scriptblock]$sql_block = {
$Query = "select * from some_table"
$CW_DBConnection = New-Object Data.SqlClient.SQLConnection
$CW_DBConnection.ConnectionString = "Data Source=someserver;Initial Catalog=some_database;Integrated Security=SSPI;"
$CW_DBConnection.Open()
$Command = New-Object Data.SqlClient.SqlCommand($Query,$CW_DBConnection)
$Adapter = New-Object Data.SqlClient.SqlDataAdapter
$DataSet = New-Object Data.DataSet
$Adapter.SelectCommand = $Command
[void]$Adapter.Fill($DataSet)
$CW_DBConnection.Close()
return $DataSet
}
Which I execute via:
$mySQLJob = Start-Job -ScriptBlock $sql_block -Credential $(Get-Credential -UserName AD\MyAdminAccount -Message "Enter Admin Password")
Wait-Job $mySQLJob
$results = Receive-Job $mySQLJob
All this goes swimmingly. However when I come to interrogate the results object, I see this :
$results
RunspaceId : 975030ec-d336-4583-9260-48439bb34292
RemotingFormat : Xml
SchemaSerializationMode : IncludeSchema
CaseSensitive : False
DefaultViewManager : {System.Data.DataViewManagerListItemTypeDescriptor}
EnforceConstraints : True
DataSetName : NewDataSet
Namespace :
Prefix :
ExtendedProperties : {}
HasErrors : False
IsInitialized : True
Locale : en-GB
Site :
Relations : {}
Tables : {System.Data.DataRow}
Container :
DesignMode : False
ContainsListCollection : True
and when I try to get to the Tables bit:
$results.Tables[0]
System.Data.DataRow
$results.Tables[0].GetType()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True ArrayList System.Object
$results.Tables[0][0]
System.Data.DataRow
$results.Tables[0][0].GetType()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True String System.Object
So literally, the result is just the string "System.Data.DataRow".
Where have I cocked up?
Note - running this from a powershell session actually executing as my Admin id and not doing it via Start-Job (i.e. just execute the SQL bits inline) works as expected, and I get actual data from the database back.
I'd already read the question at Can Powershell Receive-Job return a DataSet? when I posted this one - but apparently I didn't read it carefully enough - as the working answer returns not the top level DataSet object, but the Tables[0] property on it.
I changed my scriptblock this morning to return that, and lo-and-behold, I now get actual SQL data back.
So it appears that Receive-Job doesn't serialize the objects it returns to sufficient depth that you can return arbitrary ones (although I guess you could attempt to serialize them yourself - I've not tried that yet. UPDATE:See below).
So, in summary a one line change of
return $DataSet
to
return $DataSet.Tables[0]
Did the trick.
UPDATE: I've now tried the 'serialize it yourself' approach, and this seems to work ok. So first you update the script block to do this at the end:
$Serialized_DataSet = [System.Management.Automation.PSSerializer]::Serialize($DataSet,2)
return $Serialized_DataSet
and then when you want the results back:
$results = Receive-Job $mySQLJob
$deserialized_results = [System.Management.Automation.PSSerializer]::Deserialize($results)
and you can then see that $deserialized_results.Tables[0] actually contains results you can use.

Upload a file to Sharepoint 2010 with powershell 2.0

I'm struggling since a couple of days to upload files to Sharepoint 2010 with powershell.
I'm on a win7 machine with powershell v2 trying to upload to a SP 2010 site.
I'm having 2 major issues
$Context.web value is always empty even after Executequery() and no
error is shown. My $Context variable gets the server version (14.x.x.x.x) but nothing more
$Context.Load($variable) which always returns the error Cannot find an overload for "Load" and the argument count: "1".
I copied Sharepoint DLLs to my Win7 machine and I import the reference to my script.
The below script is a mix of many parts I took from the net.
I'v already tried unsuccessfully to add an overload on the clientcontext defining Load method without Type parameter suggested in the following post
http://soerennielsen.wordpress.com/2013/08/25/use-csom-from-powershell/
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint.Client")
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint.Client.Runtime")
$site = "https://Root-of-my-site"
$listname = "My-folder"
$context = New-Object Microsoft.SharePoint.Client.ClientContext($site)
[Microsoft.SharePoint.Client.Web]$web = $context.Web
[Microsoft.SharePoint.Client.List]$list = $web.Lists.GetByTitle($listName)
$Folder = "C:\temp\Certificates"
$List = $Context.Web.Lists.GetByTitle($listname)
Foreach ($File in (dir $Folder))
{
$FileCreationInfo = New-Object Microsoft.SharePoint.Client.FileCreationInformation
$FileCreationInfo.Overwrite = $true
$FileCreationInfo.Content = get-content -encoding byte -path $File.Fullname
$FileCreationInfo.URL = $File
$Upload = $List.RootFolder.Files.Add($FileCreationInfo)
$Context.Load($Upload)
$Context.ExecuteQuery()
}
The error is
Cannot find an overload for "Load" and the argument count: "1".
At C:\temp\uploadCertToSharepoint.ps1:48 char:14
+ $Context.Load <<<< ($Upload)
+ CategoryInfo : NotSpecified: (:) [], MethodException
+ FullyQualifiedErrorId : MethodCountCouldNotFindBest
Can someone please help me sorting this issue?
I'll need to upload around 400 files with ad-hoc fields to a sharepoint site in a couple of weeks and at the moment I'm completely stuck. Running the script server side is unfortunately not possible.
Thanks,
Marco
This error occurs since ClientRuntimeContext.Load is a Generics Method:
public void Load<T>(
T clientObject,
params Expression<Func<T, Object>>[] retrievals
)
where T : ClientObject
and Generics methods are not supported natively in PowerShell (V1, V2) AFAIK.
The workaround is to invoke a generic methods using MethodInfo.MakeGenericMethod method as described in article Invoking Generic Methods on Non-Generic Classes in PowerShell
In case of ClientRuntimeContext.Load method, the following PS function could be used:
Function Invoke-LoadMethod() {
param(
$clientObjectInstance = $(throw “Please provide an Client Object instance on which to invoke the generic method”)
)
$ctx = $clientObjectInstance.Context
$load = [Microsoft.SharePoint.Client.ClientContext].GetMethod("Load")
$type = $clientObjectInstance.GetType()
$clientObjectLoad = $load.MakeGenericMethod($type)
$clientObjectLoad.Invoke($ctx,#($clientObjectInstance,$null))
}
Then, in your example the line:
$Context.Load($Upload)
could be replaced with this one:
Invoke-LoadMethod -clientObjectInstance $Upload
References
Invoking Generic Methods on Non-Generic Classes in PowerShell
Some tips and tricks of using SharePoint Client Object Model in
PowerShell. Part 1
It throws the error because in powershell 2.0 you cannot call generic method directly.
You need to create closed method using MakeGenericMethod. Try to use code below.
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint.Client")
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint.Client.Runtime")
$site = "http://server"
$listname = "listName"
$Folder = "C:\PS\Test"
$context = New-Object Microsoft.SharePoint.Client.ClientContext($site)
[Microsoft.SharePoint.Client.Web]$web = $context.Web
[Microsoft.SharePoint.Client.List]$list = $web.Lists.GetByTitle($listName)
$method = $Context.GetType().GetMethod("Load")
$closedMethod = $method.MakeGenericMethod([Microsoft.SharePoint.Client.File])
Foreach ($File in (dir $Folder))
{
$FileCreationInfo = New-Object Microsoft.SharePoint.Client.FileCreationInformation
$FileCreationInfo.Overwrite = $true
$FileCreationInfo.Content = (get-content -encoding byte -path $File.Fullname)
$FileCreationInfo.URL = $File
$Upload = $List.RootFolder.Files.Add($FileCreationInfo)
$closedMethod.Invoke($Context, #($Upload, $null) )
$Context.ExecuteQuery()
}

System.DirectoryServices.DirectorySearcher works if called from PowerShell but not if called from cmd.exe

I wrote an script for PowerShell 1.0 (now using 2.0) that executes a search on my Active Directory. The code is the following:
$filter = "some filter"
$rootEntry = New-Object System.DirectoryServices.DirectoryEntry
$searcher = New-Object System.DirectoryServices.DirectorySearcher
$searcher.SearchRoot = $rootEntry
$searcher.Filter = $filter
$searcher.SearchScope = "Subtree"
$colResults = $searcher.FindAll()
After calling FindAll() method of the DirectorySearcher instance, I print the results to see what I got.
The thing is, if I start PowerShell.exe and call the script on the prompt I'm able to see results. But if I try to call it using cmd.exe using the same filter I don't see any results. FindAll() returns an empty result set.
I'm running this on a Windows 2003 Server. It did not came with PowerShell 1.0 so I downloaded it and installed it on the server. It does have .Net Framework 2.0.
Any suggestions?
Thanks a lot.
By defaul your $rootEntry point on the root of you local AD i you are running on a server, and this with the credetial of the current process. you don't show what is your filter and how you use your result.
Here is a small sample of an ADSI search from PowerShell
Clear-Host
# ADSI Bind with current process credentials
#$dn = [adsi] "LDAP://192.168.30.200:389/dc=dom,dc=fr"
# ADSI Bind with specific credentials
$dn = New-Object System.DirectoryServices.DirectoryEntry ("LDAP://192.168.183.138:389/dc=societe,dc=fr","administrateur#societe.fr","test.2011")
# Look for users
$Rech = new-object System.DirectoryServices.DirectorySearcher($dn)
$rc = $Rech.filter = "((objectCategory=person))"
$rc = $Rech.SearchScope = "subtree"
$rc = $Rech.PropertiesToLoad.Add("distinguishedName");
$rc = $Rech.PropertiesToLoad.Add("sAMAccountName");
$rc = $Rech.PropertiesToLoad.Add("ipphone");
$rc = $Rech.PropertiesToLoad.Add("telephoneNumber");
$rc = $Rech.PropertiesToLoad.Add("memberOf");
$rc = $Rech.PropertiesToLoad.Add("distinguishedname");
$rc = $Rech.PropertiesToLoad.Add("physicalDeliveryOfficeName"); # Your attribute
$liste = $Rech.findall()
Finally got it working by doing two things:
Upgrade to PowerShell 2.0.
Run with -File option.
So the command was run like this:
>>powershell -file ./script.ps1 "dn" "uid"
I'm not sure what the difference between the -File and -Command options are (does anyone?) but it worked.
Thanks.