diff --git a/docs/features/incremental-generators.cookbook.md b/docs/features/incremental-generators.cookbook.md
index 3cf0870f6bf57..89f92a4564f28 100644
--- a/docs/features/incremental-generators.cookbook.md
+++ b/docs/features/incremental-generators.cookbook.md
@@ -636,7 +636,190 @@ TODO: https://github.com/dotnet/roslyn/issues/72149
### Auto interface implementation
-TODO: https://github.com/dotnet/roslyn/issues/72149
+**User scenario:** As a generator author I want to be able to implement the properties of interfaces passed as arguments to a decorator of a class automatically for a user
+
+**Solution:** Require the user to decorate the class with the `[AutoImplement]` Attribute and pass as arguments the types of the interfaces they want to self-implement themselves; The classes that implement the attribute have to be `partial class`.
+Provide that attribute in a `RegisterPostInitializationOutput` step. Register for callbacks on the classes with
+`ForAttributeWithMetadataName` using the fullyQualifiedMetadataName `FullyQualifiedAttributeName`, and use tuples (or create an equatable model) to pass along that information.
+The attribute could work for structs too, the example was kept simple on purpose for the workbook sample.
+
+**Example:**
+
+```csharp
+public interface IUserInterface
+{
+ int InterfaceProperty { get; set; }
+}
+
+public interface IUserInterface2
+{
+ float InterfacePropertyOnlyGetter { get; }
+}
+
+[AutoImplementProperties(typeof(IUserInterface), typeof(IUserInterface2))]
+public partial class UserClass
+{
+ public string UserProp { get; set; }
+}
+```
+
+```csharp
+#nullable enable
+[Generator]
+public class AutoImplementGenerator : IIncrementalGenerator
+{
+ private const string AttributeNameSpace = "AttributeGenerator";
+ private const string AttributeName = "AutoImplementProperties";
+ private const string AttributeClassName = $"{AttributeName}Attribute";
+ private const string FullyQualifiedAttributeName = $"{AttributeNameSpace}.{AttributeClassName}";
+
+ public void Initialize(IncrementalGeneratorInitializationContext context)
+ {
+ context.RegisterPostInitializationOutput(ctx =>
+ {
+ //Generate the AutoImplementProperties Attribute
+ const string autoImplementAttributeDeclarationCode = $$"""
+//
+using System;
+namespace {{AttributeNameSpace}};
+
+[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
+sealed class {{AttributeClassName}} : Attribute
+{
+ public Type[] InterfacesTypes { get; }
+ public {{AttributeClassName}}(params Type[] interfacesTypes)
+ {
+ InterfacesTypes = interfacesTypes;
+ }
+}
+""";
+ ctx.AddSource($"{AttributeClassName}.g.cs", autoImplementAttributeDeclarationCode);
+ });
+
+ IncrementalValuesProvider provider = context.SyntaxProvider.ForAttributeWithMetadataName(
+ fullyQualifiedMetadataName: FullyQualifiedAttributeName,
+ predicate: static (node, cancellationToken_) => node is ClassDeclarationSyntax,
+ transform: static (ctx, cancellationToken) =>
+ {
+ ISymbol classSymbol = ctx.TargetSymbol;
+
+ return new ClassModel(
+ classSymbol.Name,
+ classSymbol.ContainingNamespace.ToDisplayString(),
+ GetInterfaceModels(ctx.Attributes[0])
+ );
+ });
+
+ context.RegisterSourceOutput(provider, static (context, classModel) =>
+ {
+ foreach (InterfaceModel interfaceModel in classModel.Interfaces)
+ {
+ StringBuilder sourceBuilder = new($$"""
+ //
+ namespace {{classModel.NameSpace}};
+
+ public partial class {{classModel.Name}} : {{interfaceModel.FullyQualifiedName}}
+ {
+
+ """);
+
+ foreach (string property in interfaceModel.Properties)
+ {
+ sourceBuilder.AppendLine(property);
+ }
+
+ sourceBuilder.AppendLine("""
+ }
+ """);
+
+ //Concat class name and interface name to have unique file name if a class implements two interfaces with AutoImplement Attribute
+ string generatedFileName = $"{classModel.Name}_{interfaceModel.FullyQualifiedName}.g.cs";
+ context.AddSource(generatedFileName, sourceBuilder.ToString());
+ }
+ });
+ }
+
+ private static EquatableList GetInterfaceModels(AttributeData attribute)
+ {
+ EquatableList ret = [];
+
+ if (attribute.ConstructorArguments.Length == 0)
+ return ret;
+
+ foreach(TypedConstant constructorArgumentValue in attribute.ConstructorArguments[0].Values)
+ {
+ if (constructorArgumentValue.Value is INamedTypeSymbol { TypeKind: TypeKind.Interface } interfaceSymbol)
+ {
+ EquatableList properties = new();
+
+ foreach (IPropertySymbol interfaceProperty in interfaceSymbol
+ .GetMembers()
+ .OfType())
+ {
+ string type = interfaceProperty.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);
+
+ //Check if property has a setter
+ string setter = interfaceProperty.SetMethod is not null
+ ? "set; "
+ : string.Empty;
+
+ properties.Add($$"""
+ public {{type}} {{interfaceProperty.Name}} { get; {{setter}}}
+ """);
+ }
+
+ ret.Add(new InterfaceModel(interfaceSymbol.ToDisplayString(), properties));
+ }
+ }
+
+ return ret;
+ }
+
+ private record ClassModel(string Name, string NameSpace, EquatableList Interfaces);
+ private record InterfaceModel(string FullyQualifiedName, EquatableList Properties);
+
+ private class EquatableList : List, IEquatable>
+ {
+ public bool Equals(EquatableList? other)
+ {
+ // If the other list is null or a different size, they're not equal
+ if (other is null || Count != other.Count)
+ {
+ return false;
+ }
+
+ // Compare each pair of elements for equality
+ for (int i = 0; i < Count; i++)
+ {
+ if (!EqualityComparer.Default.Equals(this[i], other[i]))
+ {
+ return false;
+ }
+ }
+
+ // If we got this far, the lists are equal
+ return true;
+ }
+ public override bool Equals(object obj)
+ {
+ return Equals(obj as EquatableList);
+ }
+ public override int GetHashCode()
+ {
+ return this.Select(item => item?.GetHashCode() ?? 0).Aggregate((x, y) => x ^ y);
+ }
+ public static bool operator ==(EquatableList list1, EquatableList list2)
+ {
+ return ReferenceEquals(list1, list2)
+ || list1 is not null && list2 is not null && list1.Equals(list2);
+ }
+ public static bool operator !=(EquatableList list1, EquatableList list2)
+ {
+ return !(list1 == list2);
+ }
+ }
+}
+```
## Breaking Changes: