How can you add a Certificate to WebClient in Powershell - authentication

I wan't to examine a Webpage which requires Client Side Certificate Authentication.
How can i provide my Cert from the Certstore to the Webrequest:
Is there a way to specify this in Credentials odr within the Proxy?
$webclient = New-Object Net.WebClient
# The next 5 lines are required if your network has a proxy server
$webclient.Credentials = [System.Net.CredentialCache]::DefaultCredentials
if($webclient.Proxy -ne $null) {
$webclient.Proxy.Credentials = `
[System.Net.CredentialCache]::DefaultNetworkCredentials
}
# This is the main call
$output = $webclient.DownloadString("$URL")
PS: Maybe this helps: How can you add a Certificate to WebClient (C#)? But i don't get it.. ;-)

Using the new Add-Type functionality in PowerShell v2, you can craft a custom class that you can then use to make your typical WebRequest. I have included a method on the custom class to allow you to add certificates that can be used for authentication.
PS C:\> $def = #"
public class ClientCertWebClient : System.Net.WebClient
{
System.Net.HttpWebRequest request = null;
System.Security.Cryptography.X509Certificates.X509CertificateCollection certificates = null;
protected override System.Net.WebRequest GetWebRequest(System.Uri address)
{
request = (System.Net.HttpWebRequest)base.GetWebRequest(address);
if (certificates != null)
{
request.ClientCertificates.AddRange(certificates);
}
return request;
}
public void AddCerts(System.Security.Cryptography.X509Certificates.X509Certificate[] certs)
{
if (certificates == null)
{
certificates = new System.Security.Cryptography.X509Certificates.X509CertificateCollection();
}
if (request != null)
{
request.ClientCertificates.AddRange(certs);
}
certificates.AddRange(certs);
}
}
"#
PS C:\> Add-Type -TypeDefinition $def
You would perhaps want to limit the certificates being added to just the one (or ones) you would want to use rather than just use every available certificate in the Current User store, but here is an example that just loads all of them:
PS C:\> $wc = New-Object ClientCertWebClient
PS C:\> $certs = dir cert:\CurrentUser\My
PS C:\> $wc.AddCerts($certs)
PS C:\> $wc.DownloadString("http://stackoverflow.com")

Related

Azure Pack REST API Authentication

After hours of search in Microsoft messed up API documentation for its products, i am still no where on how to authenticate a rest API request in windows azure pack distribution.
Primarily i want to create an API which automate the process of deploying virtual machine, but I cant find any documentation on how to acquire the authentication token to access the resources.
Some documentation states the use of ADFS, but don't provide any reference on the ADFS REST API for authentication.
And I don't want to use ADFS in the first place. I want to authenticate using AZURE tenant and admin interface.
In conclusion, if anyone can provide any help on the REST API authentication, it will make my day.
Thanks in advance.
You can use the following PowerShell to acquire an access token.
Add-Type -Path 'C:\Program Files\Microsoft Azure Active Directory Connect\Microsoft.IdentityModel.Clients.ActiveDirectory.dll'
$tenantID = "<the tenant id of you subscription>"
$authString = "https://login.windows.net/$tenantID"
# It must be an MFA-disabled admin.
$username = "<the username>"
$password = "<the password>"
# The resource can be https://graph.windows.net/ if you are using graph api.
# Or, https://management.azure.com/ if you are using ARM.
$resource = "https://management.core.windows.net/"
# This is the common client id.
$client_id = "1950a258-227b-4e31-a9cf-717495945fc2"
$creds = New-Object "Microsoft.IdentityModel.Clients.ActiveDirectory.UserCredential" `
-ArgumentList $username,$password
$authContext = New-Object "Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext" `
-ArgumentList $authString
$authenticationResult = $authContext.AcquireToken($resource,$client_id,$creds)
# An Authorization header can be formed like this.
$authHeader = $authenticationResult.AccessTokenType + " " + $authenticationResult.AccessToken
I am doing some similar job like you did.
static string GetAspAuthToken(string authSiteEndPoint, string userName, string password)
{
var identityProviderEndpoint = new EndpointAddress(new Uri(authSiteEndPoint + "/wstrust/issue/usernamemixed"));
var identityProviderBinding = new WS2007HttpBinding(SecurityMode.TransportWithMessageCredential);
identityProviderBinding.Security.Message.EstablishSecurityContext = false;
identityProviderBinding.Security.Message.ClientCredentialType = MessageCredentialType.UserName;
identityProviderBinding.Security.Transport.ClientCredentialType = HttpClientCredentialType.None;
var trustChannelFactory = new WSTrustChannelFactory(identityProviderBinding, identityProviderEndpoint)
{
TrustVersion = TrustVersion.WSTrust13,
};
//This line is only if we're using self-signed certs in the installation
trustChannelFactory.Credentials.ServiceCertificate.SslCertificateAuthentication = new X509ServiceCertificateAuthentication() { CertificateValidationMode = X509CertificateValidationMode.None };
trustChannelFactory.Credentials.SupportInteractive = false;
trustChannelFactory.Credentials.UserName.UserName = userName;
trustChannelFactory.Credentials.UserName.Password = password;
var channel = trustChannelFactory.CreateChannel();
var rst = new RequestSecurityToken(RequestTypes.Issue)
{
AppliesTo = new EndpointReference("http://azureservices/TenantSite"),
TokenType = "urn:ietf:params:oauth:token-type:jwt",
KeyType = KeyTypes.Bearer,
};
RequestSecurityTokenResponse rstr = null;
SecurityToken token = null;
token = channel.Issue(rst, out rstr);
var tokenString = (token as GenericXmlSecurityToken).TokenXml.InnerText;
var jwtString = Encoding.UTF8.GetString(Convert.FromBase64String(tokenString));
return jwtString;
}
Parameter "authSiteEndPoint" is your Tenant Authentication site url.
default port is 30071.
You can find some resource here:
https://msdn.microsoft.com/en-us/library/dn479258.aspx
The sample program "SampleAuthApplication" can solve your question.

How to make the website Ping ARR server and say I am going down?

I have successfully configured the ARR in Windows Azure environment, the web server instances are added to server farm.
Using Health Check option in server farm, instance that timed-out or not responding is made unhealthy.
My Question is
Instead of the ARR web farm (doing health check every 10 seconds) ping the website, is it possible or the web role itself ping back the ARR server and say I am going down ?
Is it possible to ping the ARR Server from web role and say I am going down? or this is there any best approach available.
Please suggest.
I wanted some extra notifications with our ARR setup and I put this PowerShell script together that runs once an hour and checks the health status and notify me via email when ever any hosting server was seen as unhealthy. We also use other outside resources that ping the web farm externally once an minute and alerts us when it can't be seen (Pingdom). I have a feeling you're looking for a little more than this but I hope it helps a little.
#----- First add a reference to the MWA dll ----#
$dll=[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.Web.Administration")
#----- Get the manager and config object ----#
$mgr = new-object Microsoft.Web.Administration.ServerManager
$conf = $mgr.GetApplicationHostConfiguration()
#----- Get the webFarms section ----#
$section = $conf.GetSection("webFarms")
$webFarms = $section.GetCollection()
#----- Define an array for html fragments ----#
$fragments=#()
#----- Check each webfarm ----#
foreach ($webFarm in $webFarms)
{
$Name= $webFarm.GetAttributeValue("name");
#Get the servers in the farm
$servers = $webFarm.GetCollection()
#Write-Host "Farm Name: " $Name
$fragments+= "<b>Farm Name: $Name</b>"
$fragments+="<br>"
foreach($server in $servers)
{
$ip= $server.GetAttributeValue("address")
$hostname= ([system.net.dns]::GetHostByAddress($ip)).hostname
#Get the ARR section
$arr = $server.GetChildElement("applicationRequestRouting")
$counters = $arr.GetChildElement("counters")
$isHealthy=$counters.GetAttributeValue("isHealthy")
$state= $counters.GetAttributeValue("state")
switch ($state)
{
0 {$state= "Available"}
1 {$state= "Drain"}
2 {$state= "Unavailable"}
default {$state= "Non determinato"}
}
if( $isHealthy)
{
$isHealthy="Healthy"
$fragments+="$hostname -- $ip -- $state -- $isHealthy"
$fragments+="<br>"
}
else
{
$isHealthy="Not Healthy"
$notHealthy="RED ALERT!! This is what we trained for!"
$fragments+="$hostname -- $ip -- $state -- $isHealthy"
$fragments+="<br>"
}
#Write-Host -NoNewLine $hostname " " $ip " " $state " " $isHealthy
#NEW LINE
#Write-Host
}
#NEW LINE
#Write-Host
if($notHealthy){
#write the results to HTML formated email
$smtpServer = "SMTP server"
$smtpFrom = "email address"
$smtpTo = "email address"
$messageSubject = "Unhealthy Web Server"
$message = New-Object System.Net.Mail.MailMessage $smtpfrom, $smtpto
$message.Subject = $messageSubject
$message.IsBodyHTML = $true
$message.Body = $fragments
$smtp = New-Object Net.Mail.SmtpClient($smtpServer)
$smtp.Send($message)
}
}

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()
}

Web Deploy API and Web Management Service (WMSVC)

I'm trying to translate the following WORKING command line into web deploy api (Microsoft.Web.Deployment) code:
"C:\Program Files (x86)\IIS\Microsoft Web Deploy V3\msdeploy.exe" -verb:sync -source:contentPath="\\myserver\code_to_deploy" -dest:contentPath="Default Web Site",wmsvc="mysandbox",userName="MyWebDeployUser",password="MyPassword" -allowUntrusted
My looks like this:
string srcPath = "\\myserver\code_to_deploy";
string destPath = "Default Web Site";
DeploymentBaseOptions sourceOptions = new DeploymentBaseOptions();
sourceOptions.TraceLevel = TraceLevel.Verbose;
sourceOptions.Trace += new EventHandler<DeploymentTraceEventArgs>(Src_Trace);
DeploymentBaseOptions destOptions = new DeploymentBaseOptions();
destOptions.UserName = "MyWebDeployUser";
destOptions.Password = "MyPassword";
destOptions.AddDefaultProviderSetting("contentPath", "wmsvc", "mysandbox");
destOptions.AuthenticationType = "basic";
destOptions.TraceLevel = TraceLevel.Verbose;
destOptions.Trace += new EventHandler<DeploymentTraceEventArgs>(Dest_Trace);
ServicePointManager.ServerCertificateValidationCallback = (s, c, chain, err) =>
{
return true;
};
DeploymentSyncOptions syncOptions = new DeploymentSyncOptions();
syncOptions.DeleteDestination = true;
using (DeploymentObject depObj = DeploymentManager.CreateObject(DeploymentWellKnownProvider.ContentPath, srcPath, sourceOptions))
{
var summary = depObj.SyncTo(DeploymentWellKnownProvider.IisApp, destPath, destOptions, syncOptions);
}
When the code makes the call to 'AddDefaultProviderSetting' it fails saying that wmsvc is not supported by the provider. If I remove the line I receive a 401 from the server. Any examples of doing this or other help is much appreciated.
I don't know whether you have found a solution but here is a code snippet that allows to use wmsvc for those who need it:
DeploymentBaseOptions destinationOptions = new DeploymentBaseOptions()
{
UserName = "<user_name>",
Password = "<password>",
IncludeAcls = false,
AuthenticationType = "Basic",
UseDelegation = true,
ComputerName = "https://<server>:8172/msdeploy.axd?Site=<website>"
};
// Use -allowUntrusted option
ServicePointManager.ServerCertificateValidationCallback +=
new RemoteCertificateValidationCallback(
(object s, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) => { return true; });
string package = <zip_package_fullPath>;
string parameters = <project_SetParameters_xml_fullPath>;
using (var deploymentObject = DeploymentManager.CreateObject(DeploymentWellKnownProvider.Package, package))
{
deploymentObject.SyncParameters.Load(parameters);
DeploymentSyncOptions syncOptions = new DeploymentSyncOptions();
DeploymentChangeSummary results = deploymentObject.SyncTo(destinationOptions, syncOptions);
}
It's quite hard to find documentation on these topics. Btw, I've not succeeded in using AddDefaultProviderSetting, even by creating a .exe.configSettings file and I'm not sure it's the right method to achieve what you want to.
To create a virtual application instead of a website, only .SetParameters.xml has to be changed from
<setParameter name="IIS Web Application Name" value="<WebSite>" />
to
<setParameter name="IIS Web Application Name" value="<WebSite>/<VirtualApp>" />
Hope this helps.

How to Create a TCP Client connection with a Client Certificate in Powershell

With Following code i can establish a SSL Connection:
$cert = dir cert:\CurrentUser\My | where {$_.Subject -like "*Alice*"}
$computerName = "google.com"
$port = "443"
$socket = New-Object Net.Sockets.TcpClient($computerName, $port)
$stream = $socket.GetStream()
$sslStream = New-Object System.Net.Security.SslStream $stream,$false
$sslStream.AuthenticateAsClient($computerName)
$sslStream
This Works fine. But now i wan't to add a Client Certificate for Authentication.
Think i just need to substitute
$sslStream.AuthenticateAsClient($computerName)
with
$sslStream.BeginAuthenticateAsClient($computerName,$cert,"SslProtocols",$false,"Foo" ,"Bar")
But i wasn't lucky to get the Arguments right. Can Sombody solve the Assync Calls please ;-)
Maybe i need some C# code for this?!?
Arguments are:
System.IAsyncResult BeginAuthenticateAsClient(
string targetHost,
System.Security.Cryptography.X509Certificates.X509CertificateCollection clientCertificates,
System.Security.Authentication.SslProtocols enabledSslProtocols,
bool checkCertificateRevocation,
System.AsyncCallback asyncCallback,
System.Object asyncState)
What i finally want to achieve is to list and later specify the CipherSuites the client is connected to. (I could use Wireshark i know ;-) )
Finally got it working, wasnt argument 4 but the $Cert which was no collection.
$cert = dir cert:\CurrentUser\My | where {$_.Subject -like "*alice*"}
$computerName = "google.com"
$port = "443"
[System.Security.Authentication.SslProtocols]$protocol = "ssl3"
$certcol = New-object System.Security.Cryptography.X509Certificates.X509CertificateCollection
$certcol.Add($cert)
$socket = New-Object Net.Sockets.TcpClient($computerName, $port)
$stream = $socket.GetStream()
$sslStream = New-Object System.Net.Security.SslStream $stream,$false
#$sslStream.AuthenticateAsClient($computerName)
$sslStream.AuthenticateAsClient($computerName,$certcol,$protocol,$false)
$sslStream
According to the documentation, the difference between AuthenticateAsClient and BeginAuthenticateAsClient is that the latter is for asynchronous use.
You should try AuthenticateAsClient(String, X509CertificateCollection, SslProtocols, Boolean), where the second argument is a collection of client certificates that can be used (preferably X509Certificate2, since you'll need the private key associated with the certificate for it to be usable for authentication).