mirror of
https://github.com/ansible-collections/community.general.git
synced 2025-07-23 05:10:22 -07:00
win_credential: new module to manage credentials (#48840)
* win_credential_manager: new module to manage credentials * fix sanity issues and removed CredSSP references * renamed module to win_credential * fix typo on test variable * fix sanity ignore line
This commit is contained in:
parent
10af3874b5
commit
8e92cca139
10 changed files with 2104 additions and 0 deletions
1
test/integration/targets/win_credential/aliases
Normal file
1
test/integration/targets/win_credential/aliases
Normal file
|
@ -0,0 +1 @@
|
|||
shippable/windows/group3
|
19
test/integration/targets/win_credential/defaults/main.yml
Normal file
19
test/integration/targets/win_credential/defaults/main.yml
Normal file
|
@ -0,0 +1,19 @@
|
|||
# The certificate in files/cert.pfx was generated with the following commands
|
||||
#
|
||||
# cat > client.cnf <<EOL
|
||||
# [ssl_client]
|
||||
# basicConstraints = CA:FALSE
|
||||
# nsCertType = client
|
||||
# keyUsage = digitalSignature, keyEncipherment
|
||||
# extendedKeyUsage = clientAuth
|
||||
# EOL
|
||||
#
|
||||
# openssl genrsa -aes256 -passout pass:password1 -out cert.key 2048
|
||||
# openssl req -new -subj '/CN=ansible.domain.com' -key cert.key -out cert.req -passin pass:password1
|
||||
# openssl x509 -sha256 -req -in cert.req -days 24855 -signkey cert.key -out cert.crt -extensions ssl_client -extfile client.cnf -passin pass:password1
|
||||
# openssl pkcs12 -export -in cert.crt -inkey cert.key -out cert.pfx -passin pass:password1 -passout pass:password1
|
||||
---
|
||||
test_credential_dir: '{{ win_output_dir }}\win_credential_manager'
|
||||
test_hostname: ansible.domain.com
|
||||
key_password: password1
|
||||
cert_thumbprint: 56841AAFDD19D7DF474BDA24D01D88BD8025A00A
|
BIN
test/integration/targets/win_credential/files/cert.pfx
Normal file
BIN
test/integration/targets/win_credential/files/cert.pfx
Normal file
Binary file not shown.
|
@ -0,0 +1,498 @@
|
|||
#!powershell
|
||||
|
||||
# Copyright: (c) 2018, Ansible Project
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
#AnsibleRequires -CSharpUtil Ansible.Basic
|
||||
#Requires -Module Ansible.ModuleUtils.AddType
|
||||
|
||||
$spec = @{
|
||||
options = @{
|
||||
name = @{ type = "str"; required = $true }
|
||||
type = @{ type = "str"; required = $true; choices = @("domain_password", "domain_certificate", "generic_password", "generic_certificate") }
|
||||
}
|
||||
supports_check_mode = $true
|
||||
}
|
||||
|
||||
$module = [Ansible.Basic.AnsibleModule]::Create($args, $spec)
|
||||
|
||||
$name = $module.Params.name
|
||||
$type = $module.Params.type
|
||||
|
||||
Add-CSharpType -AnsibleModule $module -References @'
|
||||
using Microsoft.Win32.SafeHandles;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.ConstrainedExecution;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
namespace Ansible.CredentialManager
|
||||
{
|
||||
internal class NativeHelpers
|
||||
{
|
||||
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
|
||||
public class CREDENTIAL
|
||||
{
|
||||
public CredentialFlags Flags;
|
||||
public CredentialType Type;
|
||||
[MarshalAs(UnmanagedType.LPWStr)] public string TargetName;
|
||||
[MarshalAs(UnmanagedType.LPWStr)] public string Comment;
|
||||
public FILETIME LastWritten;
|
||||
public UInt32 CredentialBlobSize;
|
||||
public IntPtr CredentialBlob;
|
||||
public CredentialPersist Persist;
|
||||
public UInt32 AttributeCount;
|
||||
public IntPtr Attributes;
|
||||
[MarshalAs(UnmanagedType.LPWStr)] public string TargetAlias;
|
||||
[MarshalAs(UnmanagedType.LPWStr)] public string UserName;
|
||||
|
||||
public static explicit operator Credential(CREDENTIAL v)
|
||||
{
|
||||
byte[] secret = new byte[(int)v.CredentialBlobSize];
|
||||
if (v.CredentialBlob != IntPtr.Zero)
|
||||
Marshal.Copy(v.CredentialBlob, secret, 0, secret.Length);
|
||||
|
||||
List<CredentialAttribute> attributes = new List<CredentialAttribute>();
|
||||
if (v.AttributeCount > 0)
|
||||
{
|
||||
CREDENTIAL_ATTRIBUTE[] rawAttributes = new CREDENTIAL_ATTRIBUTE[v.AttributeCount];
|
||||
Credential.PtrToStructureArray(rawAttributes, v.Attributes);
|
||||
attributes = rawAttributes.Select(x => (CredentialAttribute)x).ToList();
|
||||
}
|
||||
|
||||
string userName = v.UserName;
|
||||
if (v.Type == CredentialType.DomainCertificate || v.Type == CredentialType.GenericCertificate)
|
||||
userName = Credential.UnmarshalCertificateCredential(userName);
|
||||
|
||||
return new Credential
|
||||
{
|
||||
Type = v.Type,
|
||||
TargetName = v.TargetName,
|
||||
Comment = v.Comment,
|
||||
LastWritten = (DateTimeOffset)v.LastWritten,
|
||||
Secret = secret,
|
||||
Persist = v.Persist,
|
||||
Attributes = attributes,
|
||||
TargetAlias = v.TargetAlias,
|
||||
UserName = userName,
|
||||
Loaded = true,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct CREDENTIAL_ATTRIBUTE
|
||||
{
|
||||
[MarshalAs(UnmanagedType.LPWStr)] public string Keyword;
|
||||
public UInt32 Flags; // Set to 0 and is reserved
|
||||
public UInt32 ValueSize;
|
||||
public IntPtr Value;
|
||||
|
||||
public static explicit operator CredentialAttribute(CREDENTIAL_ATTRIBUTE v)
|
||||
{
|
||||
byte[] value = new byte[v.ValueSize];
|
||||
Marshal.Copy(v.Value, value, 0, (int)v.ValueSize);
|
||||
|
||||
return new CredentialAttribute
|
||||
{
|
||||
Keyword = v.Keyword,
|
||||
Flags = v.Flags,
|
||||
Value = value,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct FILETIME
|
||||
{
|
||||
internal UInt32 dwLowDateTime;
|
||||
internal UInt32 dwHighDateTime;
|
||||
|
||||
public static implicit operator long(FILETIME v) { return ((long)v.dwHighDateTime << 32) + v.dwLowDateTime; }
|
||||
public static explicit operator DateTimeOffset(FILETIME v) { return DateTimeOffset.FromFileTime(v); }
|
||||
public static explicit operator FILETIME(DateTimeOffset v)
|
||||
{
|
||||
return new FILETIME()
|
||||
{
|
||||
dwLowDateTime = (UInt32)v.ToFileTime(),
|
||||
dwHighDateTime = ((UInt32)v.ToFileTime() >> 32),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum CredentialCreateFlags : uint
|
||||
{
|
||||
PreserveCredentialBlob = 1,
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum CredentialFlags
|
||||
{
|
||||
None = 0,
|
||||
PromptNow = 2,
|
||||
UsernameTarget = 4,
|
||||
}
|
||||
|
||||
public enum CredMarshalType : uint
|
||||
{
|
||||
CertCredential = 1,
|
||||
UsernameTargetCredential,
|
||||
BinaryBlobCredential,
|
||||
UsernameForPackedCredential,
|
||||
BinaryBlobForSystem,
|
||||
}
|
||||
}
|
||||
|
||||
internal class NativeMethods
|
||||
{
|
||||
[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
|
||||
public static extern bool CredDeleteW(
|
||||
[MarshalAs(UnmanagedType.LPWStr)] string TargetName,
|
||||
CredentialType Type,
|
||||
UInt32 Flags);
|
||||
|
||||
[DllImport("advapi32.dll")]
|
||||
public static extern void CredFree(
|
||||
IntPtr Buffer);
|
||||
|
||||
[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
|
||||
public static extern bool CredMarshalCredentialW(
|
||||
NativeHelpers.CredMarshalType CredType,
|
||||
SafeMemoryBuffer Credential,
|
||||
out SafeCredentialBuffer MarshaledCredential);
|
||||
|
||||
[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
|
||||
public static extern bool CredReadW(
|
||||
[MarshalAs(UnmanagedType.LPWStr)] string TargetName,
|
||||
CredentialType Type,
|
||||
UInt32 Flags,
|
||||
out SafeCredentialBuffer Credential);
|
||||
|
||||
[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
|
||||
public static extern bool CredUnmarshalCredentialW(
|
||||
[MarshalAs(UnmanagedType.LPWStr)] string MarshaledCredential,
|
||||
out NativeHelpers.CredMarshalType CredType,
|
||||
out SafeCredentialBuffer Credential);
|
||||
|
||||
[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
|
||||
public static extern bool CredWriteW(
|
||||
NativeHelpers.CREDENTIAL Credential,
|
||||
NativeHelpers.CredentialCreateFlags Flags);
|
||||
}
|
||||
|
||||
internal class SafeCredentialBuffer : SafeHandleZeroOrMinusOneIsInvalid
|
||||
{
|
||||
public SafeCredentialBuffer() : base(true) { }
|
||||
|
||||
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
|
||||
protected override bool ReleaseHandle()
|
||||
{
|
||||
NativeMethods.CredFree(handle);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
internal class SafeMemoryBuffer : SafeHandleZeroOrMinusOneIsInvalid
|
||||
{
|
||||
public SafeMemoryBuffer() : base(true) { }
|
||||
public SafeMemoryBuffer(int cb) : base(true)
|
||||
{
|
||||
base.SetHandle(Marshal.AllocHGlobal(cb));
|
||||
}
|
||||
public SafeMemoryBuffer(IntPtr handle) : base(true)
|
||||
{
|
||||
base.SetHandle(handle);
|
||||
}
|
||||
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
|
||||
protected override bool ReleaseHandle()
|
||||
{
|
||||
Marshal.FreeHGlobal(handle);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public class Win32Exception : System.ComponentModel.Win32Exception
|
||||
{
|
||||
private string _exception_msg;
|
||||
public Win32Exception(string message) : this(Marshal.GetLastWin32Error(), message) { }
|
||||
public Win32Exception(int errorCode, string message) : base(errorCode)
|
||||
{
|
||||
_exception_msg = String.Format("{0} - {1} (Win32 Error Code {2}: 0x{3})", message, base.Message, errorCode, errorCode.ToString("X8"));
|
||||
}
|
||||
public override string Message { get { return _exception_msg; } }
|
||||
public static explicit operator Win32Exception(string message) { return new Win32Exception(message); }
|
||||
}
|
||||
|
||||
public enum CredentialPersist
|
||||
{
|
||||
Session = 1,
|
||||
LocalMachine = 2,
|
||||
Enterprise = 3,
|
||||
}
|
||||
|
||||
public enum CredentialType
|
||||
{
|
||||
Generic = 1,
|
||||
DomainPassword = 2,
|
||||
DomainCertificate = 3,
|
||||
DomainVisiblePassword = 4,
|
||||
GenericCertificate = 5,
|
||||
DomainExtended = 6,
|
||||
Maximum = 7,
|
||||
MaximumEx = 1007,
|
||||
}
|
||||
|
||||
public class CredentialAttribute
|
||||
{
|
||||
public string Keyword;
|
||||
public UInt32 Flags;
|
||||
public byte[] Value;
|
||||
}
|
||||
|
||||
public class Credential
|
||||
{
|
||||
public CredentialType Type;
|
||||
public string TargetName;
|
||||
public string Comment;
|
||||
public DateTimeOffset LastWritten;
|
||||
public byte[] Secret;
|
||||
public CredentialPersist Persist;
|
||||
public List<CredentialAttribute> Attributes = new List<CredentialAttribute>();
|
||||
public string TargetAlias;
|
||||
public string UserName;
|
||||
|
||||
// Used to track whether the credential has been loaded into the store or not
|
||||
public bool Loaded { get; internal set; }
|
||||
|
||||
public void Delete()
|
||||
{
|
||||
if (!Loaded)
|
||||
return;
|
||||
|
||||
if (!NativeMethods.CredDeleteW(TargetName, Type, 0))
|
||||
throw new Win32Exception(String.Format("CredDeleteW({0}) failed", TargetName));
|
||||
Loaded = false;
|
||||
}
|
||||
|
||||
public void Write(bool preserveExisting)
|
||||
{
|
||||
string userName = UserName;
|
||||
// Convert the certificate thumbprint to the string expected
|
||||
if (Type == CredentialType.DomainCertificate || Type == CredentialType.GenericCertificate)
|
||||
userName = Credential.MarshalCertificateCredential(userName);
|
||||
|
||||
NativeHelpers.CREDENTIAL credential = new NativeHelpers.CREDENTIAL
|
||||
{
|
||||
Flags = NativeHelpers.CredentialFlags.None,
|
||||
Type = Type,
|
||||
TargetName = TargetName,
|
||||
Comment = Comment,
|
||||
LastWritten = new NativeHelpers.FILETIME(),
|
||||
CredentialBlobSize = (UInt32)(Secret == null ? 0 : Secret.Length),
|
||||
CredentialBlob = IntPtr.Zero, // Must be allocated and freed outside of this to ensure no memory leaks
|
||||
Persist = Persist,
|
||||
AttributeCount = (UInt32)(Attributes.Count),
|
||||
Attributes = IntPtr.Zero, // Attributes must be allocated and freed outside of this to ensure no memory leaks
|
||||
TargetAlias = TargetAlias,
|
||||
UserName = userName,
|
||||
};
|
||||
|
||||
using (SafeMemoryBuffer credentialBlob = new SafeMemoryBuffer((int)credential.CredentialBlobSize))
|
||||
{
|
||||
if (Secret != null)
|
||||
Marshal.Copy(Secret, 0, credentialBlob.DangerousGetHandle(), Secret.Length);
|
||||
credential.CredentialBlob = credentialBlob.DangerousGetHandle();
|
||||
|
||||
// Store the CREDENTIAL_ATTRIBUTE value in a safe memory buffer and make sure we dispose in all cases
|
||||
List<SafeMemoryBuffer> attributeBuffers = new List<SafeMemoryBuffer>();
|
||||
try
|
||||
{
|
||||
int attributeLength = Attributes.Sum(a => Marshal.SizeOf(typeof(NativeHelpers.CREDENTIAL_ATTRIBUTE)));
|
||||
byte[] attributeBytes = new byte[attributeLength];
|
||||
int offset = 0;
|
||||
foreach (CredentialAttribute attribute in Attributes)
|
||||
{
|
||||
SafeMemoryBuffer attributeBuffer = new SafeMemoryBuffer(attribute.Value.Length);
|
||||
attributeBuffers.Add(attributeBuffer);
|
||||
if (attribute.Value != null)
|
||||
Marshal.Copy(attribute.Value, 0, attributeBuffer.DangerousGetHandle(), attribute.Value.Length);
|
||||
|
||||
NativeHelpers.CREDENTIAL_ATTRIBUTE credentialAttribute = new NativeHelpers.CREDENTIAL_ATTRIBUTE
|
||||
{
|
||||
Keyword = attribute.Keyword,
|
||||
Flags = attribute.Flags,
|
||||
ValueSize = (UInt32)(attribute.Value == null ? 0 : attribute.Value.Length),
|
||||
Value = attributeBuffer.DangerousGetHandle(),
|
||||
};
|
||||
int attributeStructLength = Marshal.SizeOf(typeof(NativeHelpers.CREDENTIAL_ATTRIBUTE));
|
||||
|
||||
byte[] attrBytes = new byte[attributeStructLength];
|
||||
using (SafeMemoryBuffer tempBuffer = new SafeMemoryBuffer(attributeStructLength))
|
||||
{
|
||||
Marshal.StructureToPtr(credentialAttribute, tempBuffer.DangerousGetHandle(), false);
|
||||
Marshal.Copy(tempBuffer.DangerousGetHandle(), attrBytes, 0, attributeStructLength);
|
||||
}
|
||||
Buffer.BlockCopy(attrBytes, 0, attributeBytes, offset, attributeStructLength);
|
||||
offset += attributeStructLength;
|
||||
}
|
||||
|
||||
using (SafeMemoryBuffer attributes = new SafeMemoryBuffer(attributeBytes.Length))
|
||||
{
|
||||
if (attributeBytes.Length != 0)
|
||||
Marshal.Copy(attributeBytes, 0, attributes.DangerousGetHandle(), attributeBytes.Length);
|
||||
credential.Attributes = attributes.DangerousGetHandle();
|
||||
|
||||
NativeHelpers.CredentialCreateFlags createFlags = 0;
|
||||
if (preserveExisting)
|
||||
createFlags |= NativeHelpers.CredentialCreateFlags.PreserveCredentialBlob;
|
||||
|
||||
if (!NativeMethods.CredWriteW(credential, createFlags))
|
||||
throw new Win32Exception(String.Format("CredWriteW({0}) failed", TargetName));
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
foreach (SafeMemoryBuffer attributeBuffer in attributeBuffers)
|
||||
attributeBuffer.Dispose();
|
||||
}
|
||||
}
|
||||
Loaded = true;
|
||||
}
|
||||
|
||||
public static Credential GetCredential(string target, CredentialType type)
|
||||
{
|
||||
SafeCredentialBuffer buffer;
|
||||
if (!NativeMethods.CredReadW(target, type, 0, out buffer))
|
||||
{
|
||||
int lastErr = Marshal.GetLastWin32Error();
|
||||
|
||||
// Not running with CredSSP or Become so cannot manage the user's credentials
|
||||
if (lastErr == 0x00000520) // ERROR_NO_SUCH_LOGON_SESSION
|
||||
throw new InvalidOperationException("Failed to access the user's credential store, run the module with become or CredSSP");
|
||||
else if (lastErr == 0x00000490) // ERROR_NOT_FOUND
|
||||
return null;
|
||||
throw new Win32Exception(lastErr, "CredEnumerateW() failed");
|
||||
}
|
||||
|
||||
using (buffer)
|
||||
{
|
||||
NativeHelpers.CREDENTIAL credential = (NativeHelpers.CREDENTIAL)Marshal.PtrToStructure(
|
||||
buffer.DangerousGetHandle(), typeof(NativeHelpers.CREDENTIAL));
|
||||
return (Credential)credential;
|
||||
}
|
||||
}
|
||||
|
||||
public static string MarshalCertificateCredential(string thumbprint)
|
||||
{
|
||||
// CredWriteW requires the UserName field to be the value of CredMarshalCredentialW() when writting a
|
||||
// certificate auth. This converts the UserName property to the format required.
|
||||
|
||||
// While CERT_CREDENTIAL_INFO is the correct structure, we manually marshal the data in order to
|
||||
// support different cert hash lengths in the future.
|
||||
// https://docs.microsoft.com/en-us/windows/desktop/api/wincred/ns-wincred-_cert_credential_info
|
||||
int hexLength = thumbprint.Length;
|
||||
byte[] credInfo = new byte[sizeof(UInt32) + (hexLength / 2)];
|
||||
|
||||
// First field is cbSize which is a UInt32 value denoting the size of the total structure
|
||||
Array.Copy(BitConverter.GetBytes((UInt32)credInfo.Length), credInfo, sizeof(UInt32));
|
||||
|
||||
// Now copy the byte representation of the thumbprint to the rest of the struct bytes
|
||||
for (int i = 0; i < hexLength; i += 2)
|
||||
credInfo[sizeof(UInt32) + (i / 2)] = Convert.ToByte(thumbprint.Substring(i, 2), 16);
|
||||
|
||||
IntPtr pCredInfo = Marshal.AllocHGlobal(credInfo.Length);
|
||||
Marshal.Copy(credInfo, 0, pCredInfo, credInfo.Length);
|
||||
SafeMemoryBuffer pCredential = new SafeMemoryBuffer(pCredInfo);
|
||||
|
||||
NativeHelpers.CredMarshalType marshalType = NativeHelpers.CredMarshalType.CertCredential;
|
||||
using (pCredential)
|
||||
{
|
||||
SafeCredentialBuffer marshaledCredential;
|
||||
if (!NativeMethods.CredMarshalCredentialW(marshalType, pCredential, out marshaledCredential))
|
||||
throw new Win32Exception("CredMarshalCredentialW() failed");
|
||||
using (marshaledCredential)
|
||||
return Marshal.PtrToStringUni(marshaledCredential.DangerousGetHandle());
|
||||
}
|
||||
}
|
||||
|
||||
public static string UnmarshalCertificateCredential(string value)
|
||||
{
|
||||
NativeHelpers.CredMarshalType credType;
|
||||
SafeCredentialBuffer pCredInfo;
|
||||
if (!NativeMethods.CredUnmarshalCredentialW(value, out credType, out pCredInfo))
|
||||
throw new Win32Exception("CredUnmarshalCredentialW() failed");
|
||||
|
||||
using (pCredInfo)
|
||||
{
|
||||
if (credType != NativeHelpers.CredMarshalType.CertCredential)
|
||||
throw new InvalidOperationException(String.Format("Expected unmarshalled cred type of CertCredential, received {0}", credType));
|
||||
|
||||
byte[] structSizeBytes = new byte[sizeof(UInt32)];
|
||||
Marshal.Copy(pCredInfo.DangerousGetHandle(), structSizeBytes, 0, sizeof(UInt32));
|
||||
UInt32 structSize = BitConverter.ToUInt32(structSizeBytes, 0);
|
||||
|
||||
byte[] certInfoBytes = new byte[structSize];
|
||||
Marshal.Copy(pCredInfo.DangerousGetHandle(), certInfoBytes, 0, certInfoBytes.Length);
|
||||
|
||||
StringBuilder hex = new StringBuilder((certInfoBytes.Length - sizeof(UInt32)) * 2);
|
||||
for (int i = 4; i < certInfoBytes.Length; i++)
|
||||
hex.AppendFormat("{0:x2}", certInfoBytes[i]);
|
||||
|
||||
return hex.ToString().ToUpperInvariant();
|
||||
}
|
||||
}
|
||||
|
||||
internal static void PtrToStructureArray<T>(T[] array, IntPtr ptr)
|
||||
{
|
||||
IntPtr ptrOffset = ptr;
|
||||
for (int i = 0; i < array.Length; i++, ptrOffset = IntPtr.Add(ptrOffset, Marshal.SizeOf(typeof(T))))
|
||||
array[i] = (T)Marshal.PtrToStructure(ptrOffset, typeof(T));
|
||||
}
|
||||
}
|
||||
}
|
||||
'@
|
||||
|
||||
$type = switch ($type) {
|
||||
"domain_password" { [Ansible.CredentialManager.CredentialType]::DomainPassword }
|
||||
"domain_certificate" { [Ansible.CredentialManager.CredentialType]::DomainCertificate }
|
||||
"generic_password" { [Ansible.CredentialManager.CredentialType]::Generic }
|
||||
"generic_certificate" { [Ansible.CredentialManager.CredentialType]::GenericCertificate }
|
||||
}
|
||||
|
||||
$credential = [Ansible.CredentialManager.Credential]::GetCredential($name, $type)
|
||||
if ($null -ne $credential) {
|
||||
$module.Result.exists = $true
|
||||
$module.Result.alias = $credential.TargetAlias
|
||||
$module.Result.attributes = [System.Collections.ArrayList]@()
|
||||
$module.Result.comment = $credential.Comment
|
||||
$module.Result.name = $credential.TargetName
|
||||
$module.Result.persistence = $credential.Persist.ToString()
|
||||
$module.Result.type = $credential.Type.ToString()
|
||||
$module.Result.username = $credential.UserName
|
||||
|
||||
if ($null -ne $credential.Secret) {
|
||||
$module.Result.secret = [System.Convert]::ToBase64String($credential.Secret)
|
||||
} else {
|
||||
$module.Result.secret = $null
|
||||
}
|
||||
|
||||
foreach ($attribute in $credential.Attributes) {
|
||||
$attribute_info = @{
|
||||
name = $attribute.Keyword
|
||||
}
|
||||
if ($null -ne $attribute.Value) {
|
||||
$attribute_info.data = [System.Convert]::ToBase64String($attribute.Value)
|
||||
} else {
|
||||
$attribute_info.data = $null
|
||||
}
|
||||
$module.Result.attributes.Add($attribute_info) > $null
|
||||
}
|
||||
} else {
|
||||
$module.Result.exists = $false
|
||||
}
|
||||
|
||||
$module.ExitJson()
|
||||
|
2
test/integration/targets/win_credential/meta/main.yml
Normal file
2
test/integration/targets/win_credential/meta/main.yml
Normal file
|
@ -0,0 +1,2 @@
|
|||
dependencies:
|
||||
- prepare_win_tests
|
64
test/integration/targets/win_credential/tasks/main.yml
Normal file
64
test/integration/targets/win_credential/tasks/main.yml
Normal file
|
@ -0,0 +1,64 @@
|
|||
---
|
||||
- name: ensure test dir is present
|
||||
win_file:
|
||||
path: '{{ test_credential_dir }}'
|
||||
state: directory
|
||||
|
||||
- name: copy the pfx certificate
|
||||
win_copy:
|
||||
src: cert.pfx
|
||||
dest: '{{ test_credential_dir }}\cert.pfx'
|
||||
|
||||
- name: import the pfx into the personal store
|
||||
win_certificate_store:
|
||||
path: '{{ test_credential_dir }}\cert.pfx'
|
||||
state: present
|
||||
store_location: CurrentUser
|
||||
store_name: My
|
||||
password: '{{ key_password }}'
|
||||
vars: &become_vars
|
||||
ansible_become: True
|
||||
ansible_become_method: runas
|
||||
ansible_become_user: '{{ ansible_user }}'
|
||||
ansible_become_pass: '{{ ansible_password }}'
|
||||
|
||||
- name: ensure test credentials are removed before testing
|
||||
win_credential:
|
||||
name: '{{ test_hostname }}'
|
||||
type: '{{ item }}'
|
||||
state: absent
|
||||
vars: *become_vars
|
||||
with_items:
|
||||
- domain_password
|
||||
- domain_certificate
|
||||
- generic_password
|
||||
- generic_certificate
|
||||
|
||||
- block:
|
||||
- name: run tests
|
||||
include_tasks: tests.yml
|
||||
|
||||
always:
|
||||
- name: remove the pfx from the personal store
|
||||
win_certificate_store:
|
||||
state: absent
|
||||
thumbprint: '{{ cert_thumbprint }}'
|
||||
store_location: CurrentUser
|
||||
store_name: My
|
||||
|
||||
- name: remove test credentials
|
||||
win_credential:
|
||||
name: '{{ test_hostname }}'
|
||||
type: '{{ item }}'
|
||||
state: absent
|
||||
vars: *become_vars
|
||||
with_items:
|
||||
- domain_password
|
||||
- domain_certificate
|
||||
- generic_password
|
||||
- generic_certificate
|
||||
|
||||
- name: remove test dir
|
||||
win_file:
|
||||
path: '{{ test_credential_dir }}'
|
||||
state: absent
|
588
test/integration/targets/win_credential/tasks/tests.yml
Normal file
588
test/integration/targets/win_credential/tasks/tests.yml
Normal file
|
@ -0,0 +1,588 @@
|
|||
---
|
||||
- name: fail to run the module without become
|
||||
win_credential:
|
||||
name: '{{ test_hostname }}'
|
||||
type: domain_password
|
||||
username: DOMAIN\username
|
||||
secret: password
|
||||
state: present
|
||||
register: fail_no_become
|
||||
failed_when: '"Failed to access the user''s credential store, run the module with become" not in fail_no_become.msg'
|
||||
|
||||
- name: create domain user credential (check mode)
|
||||
win_credential:
|
||||
name: '{{ test_hostname }}'
|
||||
type: domain_password
|
||||
username: DOMAIN\username
|
||||
secret: password
|
||||
state: present
|
||||
register: domain_user_check
|
||||
check_mode: True
|
||||
vars: &become_vars
|
||||
ansible_become: True
|
||||
ansible_become_method: runas
|
||||
ansible_become_user: '{{ ansible_user }}'
|
||||
ansible_become_pass: '{{ ansible_password }}'
|
||||
|
||||
- name: get result of create domain user credential (check mode)
|
||||
test_cred_facts:
|
||||
name: '{{ test_hostname }}'
|
||||
type: domain_password
|
||||
register: domain_user_actual_check
|
||||
vars: *become_vars
|
||||
|
||||
- name: asset create domain user credential (check mode)
|
||||
assert:
|
||||
that:
|
||||
- domain_user_check is changed
|
||||
- not domain_user_actual_check.exists
|
||||
|
||||
- name: create domain user credential
|
||||
win_credential:
|
||||
name: '{{ test_hostname }}'
|
||||
type: domain_password
|
||||
username: DOMAIN\username
|
||||
secret: password
|
||||
state: present
|
||||
register: domain_user
|
||||
vars: *become_vars
|
||||
|
||||
- name: get result of create domain user credential
|
||||
test_cred_facts:
|
||||
name: '{{ test_hostname }}'
|
||||
type: domain_password
|
||||
register: domain_user_actual
|
||||
vars: *become_vars
|
||||
|
||||
- name: asset create domain user credential
|
||||
assert:
|
||||
that:
|
||||
- domain_user is changed
|
||||
- domain_user_actual.exists
|
||||
- domain_user_actual.alias == None
|
||||
- domain_user_actual.attributes == []
|
||||
- domain_user_actual.comment == None
|
||||
- domain_user_actual.name == test_hostname
|
||||
- domain_user_actual.persistence == "LocalMachine"
|
||||
- domain_user_actual.secret == ""
|
||||
- domain_user_actual.type == "DomainPassword"
|
||||
- domain_user_actual.username == "DOMAIN\\username"
|
||||
|
||||
- name: create domain user credential again always update
|
||||
win_credential:
|
||||
name: '{{ test_hostname }}'
|
||||
type: domain_password
|
||||
username: DOMAIN\username
|
||||
secret: password
|
||||
state: present
|
||||
register: domain_user_again_always
|
||||
vars: *become_vars
|
||||
|
||||
- name: create domain user credential again on_create
|
||||
win_credential:
|
||||
name: '{{ test_hostname }}'
|
||||
type: domain_password
|
||||
username: DOMAIN\username
|
||||
secret: password
|
||||
update_secret: on_create
|
||||
state: present
|
||||
register: domain_user_again_on_create
|
||||
vars: *become_vars
|
||||
|
||||
- name: assert create domain user credential again
|
||||
assert:
|
||||
that:
|
||||
- domain_user_again_always is changed
|
||||
- not domain_user_again_on_create is changed
|
||||
|
||||
- name: update credential (check mode)
|
||||
win_credential:
|
||||
name: '{{ test_hostname }}'
|
||||
type: domain_password
|
||||
username: DOMAIN\username2
|
||||
alias: ansible
|
||||
attributes:
|
||||
- name: attribute 1
|
||||
data: attribute 1 value
|
||||
- name: attribute 2
|
||||
data: '{{ "attribute 2 value" | b64encode }}'
|
||||
data_format: base64
|
||||
comment: Credential comment
|
||||
persistence: enterprise
|
||||
state: present
|
||||
register: update_cred_check
|
||||
check_mode: True
|
||||
vars: *become_vars
|
||||
|
||||
- name: get result of update credential (check mode)
|
||||
test_cred_facts:
|
||||
name: '{{ test_hostname }}'
|
||||
type: domain_password
|
||||
register: update_cred_actual_check
|
||||
vars: *become_vars
|
||||
|
||||
- name: assert update credential (check mode)
|
||||
assert:
|
||||
that:
|
||||
- update_cred_check is changed
|
||||
- update_cred_actual_check.exists
|
||||
- update_cred_actual_check.alias == None
|
||||
- update_cred_actual_check.attributes == []
|
||||
- update_cred_actual_check.comment == None
|
||||
- update_cred_actual_check.name == test_hostname
|
||||
- update_cred_actual_check.persistence == "LocalMachine"
|
||||
- update_cred_actual_check.secret == ""
|
||||
- update_cred_actual_check.type == "DomainPassword"
|
||||
- update_cred_actual_check.username == "DOMAIN\\username"
|
||||
|
||||
- name: update credential
|
||||
win_credential:
|
||||
name: '{{ test_hostname }}'
|
||||
type: domain_password
|
||||
username: DOMAIN\username2
|
||||
alias: ansible
|
||||
attributes:
|
||||
- name: attribute 1
|
||||
data: attribute 1 value
|
||||
- name: attribute 2
|
||||
data: '{{ "attribute 2 value" | b64encode }}'
|
||||
data_format: base64
|
||||
comment: Credential comment
|
||||
persistence: enterprise
|
||||
state: present
|
||||
register: update_cred
|
||||
vars: *become_vars
|
||||
|
||||
- name: get result of update credential
|
||||
test_cred_facts:
|
||||
name: '{{ test_hostname }}'
|
||||
type: domain_password
|
||||
register: update_cred_actual
|
||||
vars: *become_vars
|
||||
|
||||
- name: assert update credential
|
||||
assert:
|
||||
that:
|
||||
- update_cred is changed
|
||||
- update_cred_actual.exists
|
||||
- update_cred_actual.alias == "ansible"
|
||||
- update_cred_actual.attributes|count == 2
|
||||
- update_cred_actual.attributes[0].name == "attribute 1"
|
||||
- update_cred_actual.attributes[0].data == "attribute 1 value"|b64encode
|
||||
- update_cred_actual.attributes[1].name == "attribute 2"
|
||||
- update_cred_actual.attributes[1].data == "attribute 2 value"|b64encode
|
||||
- update_cred_actual.comment == "Credential comment"
|
||||
- update_cred_actual.name == test_hostname
|
||||
- update_cred_actual.persistence == "Enterprise"
|
||||
- update_cred_actual.secret == ""
|
||||
- update_cred_actual.type == "DomainPassword"
|
||||
- update_cred_actual.username == "DOMAIN\\username2"
|
||||
|
||||
- name: update credential again
|
||||
win_credential:
|
||||
name: '{{ test_hostname }}'
|
||||
type: domain_password
|
||||
username: DOMAIN\username2
|
||||
alias: ansible
|
||||
attributes:
|
||||
- name: attribute 1
|
||||
data: attribute 1 value
|
||||
- name: attribute 2
|
||||
data: '{{ "attribute 2 value" | b64encode }}'
|
||||
data_format: base64
|
||||
comment: Credential comment
|
||||
persistence: enterprise
|
||||
state: present
|
||||
register: update_cred_again
|
||||
vars: *become_vars
|
||||
|
||||
- name: assert update credential again
|
||||
assert:
|
||||
that:
|
||||
- not update_cred_again is changed
|
||||
|
||||
- name: add new attribute
|
||||
win_credential:
|
||||
name: '{{ test_hostname }}'
|
||||
type: domain_password
|
||||
username: DOMAIN\username2
|
||||
alias: ansible
|
||||
attributes:
|
||||
- name: attribute 1
|
||||
data: attribute 1 value
|
||||
- name: attribute 2
|
||||
data: '{{ "attribute 2 value" | b64encode }}'
|
||||
data_format: base64
|
||||
- name: attribute 3
|
||||
data: attribute 3 value
|
||||
comment: Credential comment
|
||||
persistence: enterprise
|
||||
state: present
|
||||
register: add_attribute
|
||||
vars: *become_vars
|
||||
|
||||
- name: get result of add new attribute
|
||||
test_cred_facts:
|
||||
name: '{{ test_hostname }}'
|
||||
type: domain_password
|
||||
register: add_attribute_actual
|
||||
vars: *become_vars
|
||||
|
||||
- name: assert add new attribute
|
||||
assert:
|
||||
that:
|
||||
- add_attribute is changed
|
||||
- add_attribute_actual.attributes|count == 3
|
||||
- add_attribute_actual.attributes[0].name == "attribute 1"
|
||||
- add_attribute_actual.attributes[0].data == "attribute 1 value"|b64encode
|
||||
- add_attribute_actual.attributes[1].name == "attribute 2"
|
||||
- add_attribute_actual.attributes[1].data == "attribute 2 value"|b64encode
|
||||
- add_attribute_actual.attributes[2].name == "attribute 3"
|
||||
- add_attribute_actual.attributes[2].data == "attribute 3 value"|b64encode
|
||||
|
||||
- name: remove attribute
|
||||
win_credential:
|
||||
name: '{{ test_hostname }}'
|
||||
type: domain_password
|
||||
username: DOMAIN\username2
|
||||
alias: ansible
|
||||
attributes:
|
||||
- name: attribute 1
|
||||
data: attribute 1 value
|
||||
- name: attribute 2
|
||||
data: '{{ "attribute 2 value" | b64encode }}'
|
||||
data_format: base64
|
||||
comment: Credential comment
|
||||
persistence: enterprise
|
||||
state: present
|
||||
register: remove_attribute
|
||||
vars: *become_vars
|
||||
|
||||
- name: get result of remove attribute
|
||||
test_cred_facts:
|
||||
name: '{{ test_hostname }}'
|
||||
type: domain_password
|
||||
register: remove_attribute_actual
|
||||
vars: *become_vars
|
||||
|
||||
- name: assert remove attribute
|
||||
assert:
|
||||
that:
|
||||
- remove_attribute is changed
|
||||
- remove_attribute_actual.attributes|count == 2
|
||||
- remove_attribute_actual.attributes[0].name == "attribute 1"
|
||||
- remove_attribute_actual.attributes[0].data == "attribute 1 value"|b64encode
|
||||
- remove_attribute_actual.attributes[1].name == "attribute 2"
|
||||
- remove_attribute_actual.attributes[1].data == "attribute 2 value"|b64encode
|
||||
|
||||
- name: edit attribute
|
||||
win_credential:
|
||||
name: '{{ test_hostname }}'
|
||||
type: domain_password
|
||||
username: DOMAIN\username2
|
||||
alias: ansible
|
||||
attributes:
|
||||
- name: attribute 1
|
||||
data: attribute 1 value new
|
||||
- name: attribute 2
|
||||
data: '{{ "attribute 2 value" | b64encode }}'
|
||||
data_format: base64
|
||||
comment: Credential comment
|
||||
persistence: enterprise
|
||||
state: present
|
||||
register: edit_attribute
|
||||
vars: *become_vars
|
||||
|
||||
- name: get result of edit attribute
|
||||
test_cred_facts:
|
||||
name: '{{ test_hostname }}'
|
||||
type: domain_password
|
||||
register: edit_attribute_actual
|
||||
vars: *become_vars
|
||||
|
||||
- name: assert remove attribute
|
||||
assert:
|
||||
that:
|
||||
- edit_attribute is changed
|
||||
- edit_attribute_actual.attributes|count == 2
|
||||
- edit_attribute_actual.attributes[0].name == "attribute 1"
|
||||
- edit_attribute_actual.attributes[0].data == "attribute 1 value new"|b64encode
|
||||
- edit_attribute_actual.attributes[1].name == "attribute 2"
|
||||
- edit_attribute_actual.attributes[1].data == "attribute 2 value"|b64encode
|
||||
|
||||
- name: remove credential (check mode)
|
||||
win_credential:
|
||||
name: '{{ test_hostname }}'
|
||||
type: domain_password
|
||||
state: absent
|
||||
register: remove_cred_check
|
||||
check_mode: True
|
||||
vars: *become_vars
|
||||
|
||||
- name: get result of remove credential (check mode)
|
||||
test_cred_facts:
|
||||
name: '{{ test_hostname }}'
|
||||
type: domain_password
|
||||
register: remove_cred_actual_check
|
||||
vars: *become_vars
|
||||
|
||||
- name: assert remove credential (check mode)
|
||||
assert:
|
||||
that:
|
||||
- remove_cred_check is changed
|
||||
- remove_cred_actual_check.exists
|
||||
|
||||
- name: remove credential
|
||||
win_credential:
|
||||
name: '{{ test_hostname }}'
|
||||
type: domain_password
|
||||
state: absent
|
||||
register: remove_cred
|
||||
vars: *become_vars
|
||||
|
||||
- name: get result of remove credential
|
||||
test_cred_facts:
|
||||
name: '{{ test_hostname }}'
|
||||
type: domain_password
|
||||
register: remove_cred_actual
|
||||
vars: *become_vars
|
||||
|
||||
- name: assert remove credential
|
||||
assert:
|
||||
that:
|
||||
- remove_cred is changed
|
||||
- not remove_cred_actual.exists
|
||||
|
||||
- name: remove credential again
|
||||
win_credential:
|
||||
name: '{{ test_hostname }}'
|
||||
type: domain_password
|
||||
state: absent
|
||||
register: remove_cred_again
|
||||
vars: *become_vars
|
||||
|
||||
- name: assert remove credential again
|
||||
assert:
|
||||
that:
|
||||
- not remove_cred_again is changed
|
||||
|
||||
- name: create generic password (check mode)
|
||||
win_credential:
|
||||
name: '{{ test_hostname }}'
|
||||
type: generic_password
|
||||
persistence: enterprise
|
||||
username: genericuser
|
||||
secret: genericpass
|
||||
state: present
|
||||
register: generic_password_check
|
||||
check_mode: True
|
||||
vars: *become_vars
|
||||
|
||||
- name: get result of create generic password (check mode)
|
||||
test_cred_facts:
|
||||
name: '{{ test_hostname }}'
|
||||
type: generic_password
|
||||
register: generic_password_actual_check
|
||||
vars: *become_vars
|
||||
|
||||
- name: assert result of create generic password (check mode)
|
||||
assert:
|
||||
that:
|
||||
- generic_password_check is changed
|
||||
- not generic_password_actual_check.exists
|
||||
|
||||
- name: create generic password
|
||||
win_credential:
|
||||
name: '{{ test_hostname }}'
|
||||
type: generic_password
|
||||
persistence: enterprise
|
||||
username: genericuser
|
||||
secret: genericpass
|
||||
state: present
|
||||
register: generic_password
|
||||
vars: *become_vars
|
||||
|
||||
- name: get result of create generic password
|
||||
test_cred_facts:
|
||||
name: '{{ test_hostname }}'
|
||||
type: generic_password
|
||||
register: generic_password_actual
|
||||
vars: *become_vars
|
||||
|
||||
- name: assert create generic password
|
||||
assert:
|
||||
that:
|
||||
- generic_password is changed
|
||||
- generic_password_actual.exists
|
||||
- generic_password_actual.alias == None
|
||||
- generic_password_actual.attributes == []
|
||||
- generic_password_actual.comment == None
|
||||
- generic_password_actual.name == test_hostname
|
||||
- generic_password_actual.persistence == "Enterprise"
|
||||
- generic_password_actual.secret == "genericpass"|b64encode
|
||||
- generic_password_actual.type == "Generic"
|
||||
- generic_password_actual.username == "genericuser"
|
||||
|
||||
- name: create generic password again
|
||||
win_credential:
|
||||
name: '{{ test_hostname }}'
|
||||
type: generic_password
|
||||
persistence: enterprise
|
||||
username: genericuser
|
||||
secret: genericpass
|
||||
state: present
|
||||
register: generic_password_again
|
||||
vars: *become_vars
|
||||
|
||||
- name: assert create generic password again
|
||||
assert:
|
||||
that:
|
||||
- not generic_password_again is changed
|
||||
|
||||
- name: fail to create certificate cred with invalid thumbprint
|
||||
win_credential:
|
||||
name: '{{ test_hostname }}'
|
||||
type: domain_certificate
|
||||
username: 00112233445566778899AABBCCDDEEFF00112233
|
||||
state: present
|
||||
register: fail_invalid_cert
|
||||
failed_when: fail_invalid_cert.msg != "Failed to find certificate with the thumbprint 00112233445566778899AABBCCDDEEFF00112233 in the CurrentUser\\My store"
|
||||
vars: *become_vars
|
||||
|
||||
- name: create domain certificate cred (check mode)
|
||||
win_credential:
|
||||
name: '{{ test_hostname }}'
|
||||
type: domain_certificate
|
||||
username: '{{ cert_thumbprint }}'
|
||||
state: present
|
||||
register: domain_cert_check
|
||||
check_mode: True
|
||||
vars: *become_vars
|
||||
|
||||
- name: get result of create domain certificate cred (check mode)
|
||||
test_cred_facts:
|
||||
name: '{{ test_hostname }}'
|
||||
type: domain_certificate
|
||||
register: domain_cert_actual_check
|
||||
vars: *become_vars
|
||||
|
||||
- name: assert create domain certificate cred (check mode)
|
||||
assert:
|
||||
that:
|
||||
- domain_cert_check is changed
|
||||
- not domain_cert_actual_check.exists
|
||||
|
||||
- name: create domain certificate cred
|
||||
win_credential:
|
||||
name: '{{ test_hostname }}'
|
||||
type: domain_certificate
|
||||
username: '{{ cert_thumbprint }}'
|
||||
state: present
|
||||
register: domain_cert
|
||||
vars: *become_vars
|
||||
|
||||
- name: get result of create domain certificate cred
|
||||
test_cred_facts:
|
||||
name: '{{ test_hostname }}'
|
||||
type: domain_certificate
|
||||
register: domain_cert_actual
|
||||
vars: *become_vars
|
||||
|
||||
- name: assert create domain certificate cred
|
||||
assert:
|
||||
that:
|
||||
- domain_cert is changed
|
||||
- domain_cert_actual.exists
|
||||
- domain_cert_actual.alias == None
|
||||
- domain_cert_actual.attributes == []
|
||||
- domain_cert_actual.comment == None
|
||||
- domain_cert_actual.name == test_hostname
|
||||
- domain_cert_actual.persistence == "LocalMachine"
|
||||
- domain_cert_actual.secret == ""
|
||||
- domain_cert_actual.type == "DomainCertificate"
|
||||
- domain_cert_actual.username == cert_thumbprint
|
||||
|
||||
- name: create domain certificate cred again
|
||||
win_credential:
|
||||
name: '{{ test_hostname }}'
|
||||
type: domain_certificate
|
||||
username: '{{ cert_thumbprint }}'
|
||||
state: present
|
||||
register: domain_cert_again
|
||||
vars: *become_vars
|
||||
|
||||
- name: assert create domain certificate cred again
|
||||
assert:
|
||||
that:
|
||||
- not domain_cert_again is changed
|
||||
|
||||
- name: create generic certificate cred (check mode)
|
||||
win_credential:
|
||||
name: '{{ test_hostname }}'
|
||||
type: generic_certificate
|
||||
username: '{{ cert_thumbprint }}'
|
||||
secret: '{{ "pin code" | b64encode }}'
|
||||
secret_format: base64
|
||||
state: present
|
||||
register: generic_cert_check
|
||||
check_mode: True
|
||||
vars: *become_vars
|
||||
|
||||
- name: get result of create generic certificate cred (check mode)
|
||||
test_cred_facts:
|
||||
name: '{{ test_hostname }}'
|
||||
type: generic_certificate
|
||||
register: generic_cert_actual_check
|
||||
vars: *become_vars
|
||||
|
||||
- name: assert create generic certificate cred (check mode)
|
||||
assert:
|
||||
that:
|
||||
- generic_cert_check is changed
|
||||
- not generic_cert_actual_check.exists
|
||||
|
||||
- name: create generic certificate cred
|
||||
win_credential:
|
||||
name: '{{ test_hostname }}'
|
||||
type: generic_certificate
|
||||
username: '{{ cert_thumbprint }}'
|
||||
secret: '{{ "pin code" | b64encode }}'
|
||||
secret_format: base64
|
||||
state: present
|
||||
register: generic_cert
|
||||
vars: *become_vars
|
||||
|
||||
- name: get result of create generic certificate cred
|
||||
test_cred_facts:
|
||||
name: '{{ test_hostname }}'
|
||||
type: generic_certificate
|
||||
register: generic_cert_actual
|
||||
vars: *become_vars
|
||||
|
||||
- name: assert create generic certificate cred
|
||||
assert:
|
||||
that:
|
||||
- generic_cert is changed
|
||||
- generic_cert_actual.exists
|
||||
- generic_cert_actual.alias == None
|
||||
- generic_cert_actual.attributes == []
|
||||
- generic_cert_actual.comment == None
|
||||
- generic_cert_actual.name == test_hostname
|
||||
- generic_cert_actual.persistence == "LocalMachine"
|
||||
- generic_cert_actual.secret == "pin code" | b64encode
|
||||
- generic_cert_actual.type == "GenericCertificate"
|
||||
- generic_cert_actual.username == cert_thumbprint
|
||||
|
||||
- name: create generic certificate cred again
|
||||
win_credential:
|
||||
name: '{{ test_hostname }}'
|
||||
type: generic_certificate
|
||||
username: '{{ cert_thumbprint }}'
|
||||
state: present
|
||||
register: generic_cert_again
|
||||
vars: *become_vars
|
||||
|
||||
- name: assert create generic certificate cred again
|
||||
assert:
|
||||
that:
|
||||
- not generic_cert_again is changed
|
Loading…
Add table
Add a link
Reference in a new issue